Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> Config config = new Config( new JSTypeRegistry(NullErrorReporter.forOldRhino()), Sets.<String>newHashSet(), false); JsDocInfoParser parser = new JsDocInfoParser( new JsDocTokenStream(typeString), "typeparsing", config, NullErrorReporter.forNewRhino()); return parser.parseTopLevelTypeExpression(parser.next()); } /** * Parses a {@link JSDocInfo} object. This parsing method reads all tokens * returned by the {@link JsDocTokenStream#getJsDocToken()} method until the * {@link JsDocToken#EOC} is returned. * * @return {@code true} if JSDoc information was correctly parsed, * {@code false} otherwise */ boolean parse() { int lineno; int charno; // JSTypes are represented as Rhino AST nodes, and then resolved later. JSTypeExpression type; state = State.SEARCHING_ANNOTATION; JsDocToken token = next(); ExtractionInfo blockInfo = extractBlockComment(token); token = blockInfo.token; // If we have a block level comment, record it. if (blockInfo.string.length() > 0) { jsdocBuilder.recordBlockDescription(blockInfo.string); } // Parse the actual JsDoc. retry: for (;;) { switch (token) { case ANNOTATION: if (state == State.SEARCHING_ANNOTATION) { state = State.SEARCHING_NEWLINE; lineno = stream.getLineno(); charno = stream.getCharno(); String annotationName = stream.getString(); Annotation annotation = annotationNames.get(annotationName); if (annotation == null) { parser.addWarning("msg.bad.jsdoc.tag", annotationName, stream.getLineno(), stream.getCharno()); } else { // Mark the beginning of the annotation. jsdocBuilder.markAnnotation(annotationName, lineno, charno); switch (annotation) { case AUTHOR: ExtractionInfo authorInfo = extractSingleLineBlock(); String author = authorInfo.string; if (author.length() == 0) { parser.addWarning("msg.js

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>Block(token); String description = descriptionInfo.string; jsdocBuilder.recordDescription(description); token = descriptionInfo.token; continue retry; } case FILE_OVERVIEW: ExtractionInfo fileOverviewInfo = extractMultilineTextualBlock(token, WhitespaceOption.TRIM); String fileOverview = fileOverviewInfo.string; if (!jsdocBuilder.recordFileOverview(fileOverview) || fileOverviewJSDocInfo != null) { parser.addWarning("msg.jsdoc.fileoverview.extra", stream.getLineno(), stream.getCharno()); } token = fileOverviewInfo.token; continue retry; case LICENSE: case PRESERVE: ExtractionInfo preserveInfo = extractMultilineTextualBlock(token, WhitespaceOption.PRESERVE); String preserve = preserveInfo.string; if (preserve.length() > 0) { if (fileLevelJsDocBuilder != null) { fileLevelJsDocBuilder.append(preserve); } } token = preserveInfo.token; continue retry; case ENUM: token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); type = null; if (token != JsDocToken.EOL && token != JsDocToken.EOC) { type = createJSTypeExpression( parseAndRecordTypeNode(token)); } if (type == null) { type = createJSTypeExpression(newStringNode("number")); } if (!jsdocBuilder.recordEnumParameterType(type)) { parser.addWarning("msg.jsdoc.incompat.type", lineno, charno); } token = eatTokensUntilEOL(token); continue retry; case EXPORT: if (!jsdocBuilder.recordExport()) { parser.addWarning("msg.jsdoc.export", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case EXTERNS: if (!jsdocBuilder.recordExterns()) { parser.addWarning("msg.jsdoc.externs", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>; case JAVA_DISPATCH: if (!jsdocBuilder.recordJavaDispatch()) { parser.addWarning("msg.jsdoc.javadispatch", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case EXTENDS: case IMPLEMENTS: skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); boolean matchingRc = false; if (token == JsDocToken.LC) { token = next(); matchingRc = true; } if (token == JsDocToken.STRING) { Node typeNode = parseAndRecordTypeNameNode( token, lineno, charno, matchingRc); lineno = stream.getLineno(); charno = stream.getCharno(); typeNode = wrapNode(Token.BANG, typeNode); if (typeNode != null && !matchingRc) { typeNode.putBooleanProp(Node.BRACELESS_TYPE, true); } type = createJSTypeExpression(typeNode); if (annotation == Annotation.EXTENDS) { if (!jsdocBuilder.recordBaseType(type)) { parser.addWarning( "msg.jsdoc.incompat.type", lineno, charno); } } else { Preconditions.checkState( annotation == Annotation.IMPLEMENTS); if (!jsdocBuilder.recordImplementedInterface(type)) { parser.addWarning("msg.jsdoc.implements.duplicate", lineno, charno); } } token = next(); if (matchingRc) { if (token != JsDocToken.RC) { parser.addWarning("msg.jsdoc.missing.rc", stream.getLineno(), stream.getCharno()); } } else if (token != JsDocToken.EOL && token != JsDocToken.EOF && token != JsDocToken.EOC) { parser.addWarning("msg.end.annotation.expected", stream.getLineno(), stream.getCharno()); } } else { parser.addWarning("msg.no.type.name", lineno, charno); } token = eatTokensUntilEOL(token); continue retry; case

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> HIDDEN: if (!jsdocBuilder.recordHiddenness()) { parser.addWarning("msg.jsdoc.hidden", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case NO_ALIAS: if (!jsdocBuilder.recordNoAlias()) { parser.addWarning("msg.jsdoc.noalias", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case NO_TYPE_CHECK: if (!jsdocBuilder.recordNoTypeCheck()) { parser.addWarning("msg.jsdoc.nocheck", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case NOT_IMPLEMENTED: token = eatTokensUntilEOL(); continue retry; case INHERIT_DOC: case OVERRIDE: if (!jsdocBuilder.recordOverride()) { parser.addWarning("msg.jsdoc.override", stream.getLineno(), stream.getCharno()); } token = eatTokensUntilEOL(); continue retry; case THROWS: skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); type = null; if (token == JsDocToken.LC) { type = createJSTypeExpression( parseAndRecordTypeNode(token)); if (type == null) { // parsing error reported during recursive descent // recovering parsing token = eatTokensUntilEOL(); continue retry; } } // *Update* the token to that after the type annotation. token = current(); // Save the throw type. jsdocBuilder.recordThrowType(type); // Find the throw's description (if applicable). ExtractionInfo descriptionInfo = extractMultilineTextualBlock(token); String description = descriptionInfo.string; if (description.length() > 0) { jsdocBuilder.recordThrowDescription(type, description); } token = descriptionInfo.token; continue retry; case PARAM: skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>(); type = null; if (token == JsDocToken.LC) { type = createJSTypeExpression( parseAndRecordParamTypeNode(token)); if (type == null) { // parsing error reported during recursive descent // recovering parsing token = eatTokensUntilEOL(); continue retry; } skipEOLs(); token = next(); lineno = stream.getLineno(); charno = stream.getCharno(); } String name = null; boolean isBracketedParam = JsDocToken.LB == token; if (isBracketedParam) { token = next(); } if (JsDocToken.STRING != token) { parser.addWarning("msg.missing.variable.name", lineno, charno); } else { name = stream.getString(); if (isBracketedParam) { token = next(); // Throw out JsDocToolkit's "default" parameter annotation. // It makes no sense under our type system. if (JsDocToken.EQUALS == token) { token = next(); if (JsDocToken.STRING == token) { token = next(); } } if (JsDocToken.RB != token) { reportTypeSyntaxWarning("msg.jsdoc.missing.rb"); } else if (type != null) { // Make the type expression optional, if it isn't // already. type = JSTypeExpression.makeOptionalArg(type); } } // If the param name has a DOT in it, just throw it out // quietly. We do not handle the JsDocToolkit method // for handling properties of params. if (name.indexOf('.') > -1) { name = null; } else if (!jsdocBuilder.recordParameter(name, type)) { if (jsdocBuilder.hasParameter(name)) { parser.addWarning("msg.dup.variable.name", name, lineno, charno); } else { parser.addWarning("msg.jsdoc.incompat.type", name, lineno, charno); } } } if (name == null) { token = eatTokensUntilEOL(token); continue retry; } jsdocBuilder.markName(name, lineno, char

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> eatTokensUntilEOL(); continue retry; case SEE: ExtractionInfo referenceInfo = extractSingleLineBlock(); String reference = referenceInfo.string; if (reference.length() == 0) { parser.addWarning("msg.jsdoc.seemissing", stream.getLineno(), stream.getCharno()); } else { jsdocBuilder.addReference(reference); } token = referenceInfo.token; continue retry; case SUPPRESS: token = parseSuppressTag(next()); continue retry; case TEMPLATE: ExtractionInfo templateInfo = extractSingleLineBlock(); String templateTypeName = templateInfo.string; if (templateTypeName.length() == 0) { parser.addWarning("msg.jsdoc.templatemissing", stream.getLineno(), stream.getCharno()); } else if (!jsdocBuilder.recordTemplateTypeName( templateTypeName)) { parser.addWarning("msg.jsdoc.template.at.most.once", stream.getLineno(), stream.getCharno()); } token = templateInfo.token; continue retry; case VERSION: ExtractionInfo versionInfo = extractSingleLineBlock(); String version = versionInfo.string; if (version.length() == 0) { parser.addWarning("msg.jsdoc.versionmissing", stream.getLineno(), stream.getCharno()); } else { if (!jsdocBuilder.recordVersion(version)) { parser.addWarning("msg.jsdoc.extraversion", stream.getLineno(), stream.getCharno()); } } token = versionInfo.token; continue retry; case DEFINE: case RETURN: case THIS: case TYPE: case TYPEDEF: skipEOLs(); lineno = stream.getLineno(); charno = stream.getCharno(); token = next(); Node typeNode = parseAndRecordTypeNode(token, lineno, charno); if (annotation == Annotation.THIS) { typeNode = wrapNode(Token.BANG, typeNode); if (typeNode != null && token != JsDocToken.LC) { typeNode.putBooleanProp(Node.BRACELESS_TYPE, true); } } type =

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> false; case EOL: if (state == State.SEARCHING_NEWLINE) { state = State.SEARCHING_ANNOTATION; } token = next(); continue retry; default: if (token == JsDocToken.STAR && state == State.SEARCHING_ANNOTATION) { token = next(); continue retry; } else { state = State.SEARCHING_NEWLINE; token = eatTokensUntilEOL(); continue retry; } } // next token token = next(); } } /** * Parse a {@code @suppress} tag of the form * {@code @suppress&#123;warning1|warning2&#125;}. * * @param token The current token. */ private JsDocToken parseSuppressTag(JsDocToken token) { if (token == JsDocToken.LC) { Set<String> suppressions = new HashSet<String>(); while (true) { if (match(JsDocToken.STRING)) { suppressions.add(stream.getString()); token = next(); } else { parser.addWarning("msg.jsdoc.suppress", stream.getLineno(), stream.getCharno()); return token; } if (match(JsDocToken.PIPE)) { token = next(); } else { break; } } if (!match(JsDocToken.RC)) { parser.addWarning("msg.jsdoc.suppress", stream.getLineno(), stream.getCharno()); } else { token = next(); if (!jsdocBuilder.recordSuppressions(suppressions)) { parser.addWarning("msg.jsdoc.suppress.duplicate", stream.getLineno(), stream.getCharno()); } } } return token; } /** * Looks for a type expression at the current token and if found, * returns it. Note that this method consumes input. * * @param token The current token. * @return The type expression found or null if none. */ private Node parseAndRecordTypeNode(JsDocToken token) { return parseAndRecordTypeNode(token, token == JsDocToken.LC); } /** * Looks for

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> a type expression at the current token and if found, * returns it. Note that this method consumes input. * * @param token The current token. * @param matchingLC Whether the type expression starts with a "{". * @return The type expression found or null if none. */ private Node parseAndRecordTypeNode(JsDocToken token, boolean matchingLC) { return parseAndRecordTypeNode(token, stream.getLineno(), stream.getCharno(), matchingLC, false); } /** * Looks for a type expression at the current token and if found, * returns it. Note that this method consumes input. * * @param token The current token. * @param lineno The line of the type expression. * @param startCharno The starting character position of the type expression. * @return The type expression found or null if none. */ private Node parseAndRecordTypeNode(JsDocToken token, int lineno, int startCharno) { return parseAndRecordTypeNode(token, lineno, startCharno, token == JsDocToken.LC, false); } /** * Looks for a type expression at the current token and if found, * returns it. Note that this method consumes input. * * @param token The current token. * @param lineno The line of the type expression. * @param startCharno The starting character position of the type expression. * @param matchingLC Whether the type expression starts with a "{". * @return The type expression found or null if none. */ private Node parseAndRecordTypeNameNode(JsDocToken token, int lineno, int startCharno, boolean matchingLC) { return parseAndRecordTypeNode(token, lineno, startCharno, matchingLC, true); } /** * Looks for a type expression at the current token and if found, * returns it. Note that this method consumes input. * * Parameter type expressions are special for two reasons: * <ol> * <li>They must begin with '{', to distinguish type names from param names. * <li>They may end in '=', to denote optionality. * </ol> * * @param token The current token. * @return The type expression

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> found or null if none. */ private Node parseAndRecordParamTypeNode(JsDocToken token) { Preconditions.checkArgument(token == JsDocToken.LC); int lineno = stream.getLineno(); int startCharno = stream.getCharno(); Node typeNode = parseParamTypeExpressionAnnotation(token); int endCharno = stream.getCharno(); jsdocBuilder.markTypeNode(typeNode, lineno, startCharno, endCharno, true); return typeNode; } /** * Looks for a parameter type expression at the current token and if found, * returns it. Note that this method consumes input. * * @param token The current token. * @param lineno The line of the type expression. * @param startCharno The starting character position of the type expression. * @param matchingLC Whether the type expression starts with a "{". * @param onlyParseSimpleNames If true, only simple type names are parsed * (via a call to parseTypeNameAnnotation instead of * parseTypeExpressionAnnotation). * @return The type expression found or null if none. */ private Node parseAndRecordTypeNode(JsDocToken token, int lineno, int startCharno, boolean matchingLC, boolean onlyParseSimpleNames) { Node typeNode = null; if (onlyParseSimpleNames) { typeNode = parseTypeNameAnnotation(token); } else { typeNode = parseTypeExpressionAnnotation(token); } if (typeNode != null && !matchingLC) { typeNode.putBooleanProp(Node.BRACELESS_TYPE, true); } int endCharno = stream.getCharno(); jsdocBuilder.markTypeNode(typeNode, lineno, startCharno, endCharno, matchingLC); return typeNode; } /** * Determines whether the given type is a valid {@code @define} type. */ // TODO(nicksantos): Move this into a check pass. private boolean isValidDefineType(Node typeNode) { JSType type = typeRegistry.createFromTypeNodes(typeNode, "", null); return !type.isUnknownType() && type.isSubtype( typeRegistry.getNativeType(JSTypeNative.NUMBER_STRING_

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>BOOLEAN)); } /** * Converts a JSDoc token to its string representation. */ private String toString(JsDocToken token) { switch (token) { case ANNOTATION: return "@" + stream.getString(); case BANG: return "!"; case COMMA: return ","; case COLON: return ":"; case GT: return ">"; case LB: return "["; case LC: return "{"; case LP: return "("; case LT: return ".<"; case QMARK: return "?"; case PIPE: return "|"; case RB: return "]"; case RC: return "}"; case RP: return ")"; case STAR: return "*"; case ELLIPSIS: return "..."; case EQUALS: return "="; case STRING: return stream.getString(); default: throw new IllegalStateException(token.toString()); } } /** * Constructs a new {@code JSTypeExpression}. * @param n A node. May be null. */ private JSTypeExpression createJSTypeExpression(Node n) { return n == null ? null : new JSTypeExpression(n, sourceName, typeRegistry); } /** * Tuple for returning both the string extracted and the * new token following a call to any of the extract*Block * methods. */ private static class ExtractionInfo { private final String string; private final JsDocToken token; public ExtractionInfo(String string, JsDocToken token) { this.string = string; this.token = token; } public String getString() { return string; } public JsDocToken getToken() { return token; } } /** * Extracts the text found on the current line starting at token. Note that * token = token.info; should be called after this method is used to update * the token properly in the parser. * * @return The extraction information. */ private ExtractionInfo extractSingleLineBlock() { // Get the current starting point. stream.update(); int lineno = stream.getLineno(); int charno = stream.getCharno() + 1; String line = stream.get

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>RemainingJSDocLine().trim(); // Record the textual description. if (line.length() > 0) { jsdocBuilder.markText(line, lineno, charno, lineno, charno + line.length()); } return new ExtractionInfo(line, next()); } private ExtractionInfo extractMultilineTextualBlock(JsDocToken token) { return extractMultilineTextualBlock(token, WhitespaceOption.SINGLE_LINE); } private enum WhitespaceOption { /** * Preserves all whitespace and formatting. Needed for licenses and * purposely formatted text. */ PRESERVE, /** Preserves newlines but trims the output. */ TRIM, /** Removes newlines and turns the output into a single line string. */ SINGLE_LINE } /** * Extracts the text found on the current line and all subsequent * until either an annotation, end of comment or end of file is reached. * Note that if this method detects an end of line as the first token, it * will quit immediately (indicating that there is no text where it was * expected). Note that token = info.token; should be called after this * method is used to update the token properly in the parser. * * @param token The start token. * @param option How to handle whitespace. * * @return The extraction information. */ @SuppressWarnings("fallthrough") private ExtractionInfo extractMultilineTextualBlock(JsDocToken token, WhitespaceOption option) { if (token == JsDocToken.EOC || token == JsDocToken.EOL || token == JsDocToken.EOF) { return new ExtractionInfo("", token); } stream.update(); int startLineno = stream.getLineno(); int startCharno = stream.getCharno() + 1; // Read the content from the first line. String line = stream.getRemainingJSDocLine(); if (option != WhitespaceOption.PRESERVE) { line = line.trim(); } StringBuilder builder = new StringBuilder(); builder.append(line); state = State.SEARCHING_ANNOTATION; token = next(); boolean ignoreStar = false; do {

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> switch (token) { case STAR: if (!ignoreStar) { if (builder.length() > 0) { builder.append(line); token = next(); } } while (true); } /** * Extracts the top-level block comment from the JsDoc comment, if any. * This method differs from the extractMultilineTextualBlock in that it * terminates under different conditions (it doesn't have the same * prechecks), it does not first read in the remaining of the current * line and its conditions for ignoring the "*" (STAR) are different. * * @param token The starting token. * * @return The extraction information. */ private ExtractionInfo extractBlockComment(JsDocToken token) { StringBuilder builder = new StringBuilder(); boolean ignoreStar = true; do { switch (token) { case ANNOTATION: case EOC: case EOF: return new ExtractionInfo(builder.toString().trim(), token); case STAR: if (!ignoreStar) { if (builder.length() > 0) { builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: ignoreStar = true; builder.append('\n'); token = next(); continue; default: if (!ignoreStar && builder.length() > 0) { builder.append(' '); } ignoreStar = false; builder.append(toString(token)); String line = stream.getRemainingJSDocLine(); line = trimEnd(line); builder.append(line); token = next(); } } while (true); } /** * Trim characters from only the end of a string. * This method will remove all whitespace characters * (defined by Character.isWhitespace(char), in addition to the characters * provided, from the end of the provided string. * * @param s String to be trimmed * @return String with whitespace and characters in extraChars removed * from the end. */ private static String trimEnd(String s) { int trimCount = 0; while (trimCount < s.length()) { char ch = s.charAt(s.

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>length() - trimCount - 1); if (Character.isWhitespace(ch)) { trimCount++; } else { break; } } if (trimCount == 0) { return s; } return s.substring(0, s.length() - trimCount); } // Based on ES4 grammar proposed on July 10, 2008. // http://wiki.ecmascript.org/doku.php?id=spec:spec // Deliberately written to line up with the actual grammar rules, // for maximum flexibility. // TODO(nicksantos): The current implementation tries to maintain backwards // compatibility with previous versions of the spec whenever we can. // We should try to gradually withdraw support for these. /** * TypeExpressionAnnotation := TypeExpression | * '{' TopLevelTypeExpression '}' */ private Node parseTypeExpressionAnnotation(JsDocToken token) { if (token == JsDocToken.LC) { skipEOLs(); Node typeNode = parseTopLevelTypeExpression(next()); if (typeNode != null) { skipEOLs(); if (!match(JsDocToken.RC)) { reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); } else { next(); } } return typeNode; } else { return parseTypeExpression(token); } } /** * ParamTypeExpressionAnnotation := * '{' OptionalParameterType '}' | * '{' TopLevelTypeExpression '}' | * '{' '...' TopLevelTypeExpression '}' * * OptionalParameterType := * TopLevelTypeExpression '=' */ private Node parseParamTypeExpressionAnnotation(JsDocToken token) { Preconditions.checkArgument(token == JsDocToken.LC); skipEOLs(); boolean restArg = false; token = next(); if (token == JsDocToken.ELLIPSIS) { token = next(); if (token == JsDocToken.RC) { // EMPTY represents the UNKNOWN type in the Type AST. return wrapNode(Token.ELLIPSIS, new Node(Token.EMPTY)); } restArg = true; } Node typeNode = parseTopLevelTypeExpression(token); if (

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>typeNode != null) { skipEOLs(); if (restArg) { typeNode = wrapNode(Token.ELLIPSIS, typeNode); } else if (match(JsDocToken.EQUALS)) { next(); skipEOLs(); typeNode = wrapNode(Token.EQUALS, typeNode); } if (!match(JsDocToken.RC)) { reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); } else { next(); } } return typeNode; } /** * TypeNameAnnotation := TypeName | '{' TypeName '}' */ private Node parseTypeNameAnnotation(JsDocToken token) { if (token == JsDocToken.LC) { skipEOLs(); Node typeNode = parseTypeName(next()); if (typeNode != null) { skipEOLs(); if (!match(JsDocToken.RC)) { reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); } else { next(); } } return typeNode; } else { return parseTypeName(token); } } /** * TopLevelTypeExpression := TypeExpression * | TypeUnionList * * We made this rule up, for the sake of backwards compatibility. */ private Node parseTopLevelTypeExpression(JsDocToken token) { Node typeExpr = parseTypeExpression(token); if (typeExpr != null) { // top-level unions are allowed if (match(JsDocToken.PIPE)) { next(); if (match(JsDocToken.PIPE)) { // We support double pipes for backwards-compatibility. next(); } skipEOLs(); token = next(); return parseUnionTypeWithAlternate(token, typeExpr); } } return typeExpr; } /** * TypeExpressionList := TopLevelTypeExpression * | TopLevelTypeExpression ',' TypeExpressionList */ private Node parseTypeExpressionList(JsDocToken token) { Node typeExpr = parseTopLevelTypeExpression(token); if (typeExpr == null) { return null; } Node typeList = new Node(Token.BLOCK); typeList.addChildToBack(typeExpr); while (match(JsDocToken.COMMA))

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> { next(); skipEOLs(); typeExpr = parseTopLevelTypeExpression(next()); if (typeExpr == null) { return null; } typeList.addChildToBack(typeExpr); } return typeList; } /** * TypeExpression := BasicTypeExpression * | '?' BasicTypeExpression * | '!' BasicTypeExpression * | BasicTypeExpression '?' * | BasicTypeExpression '!' */ private Node parseTypeExpression(JsDocToken token) { if (token == JsDocToken.QMARK) { return wrapNode(Token.QMARK, parseBasicTypeExpression(next())); } else if (token == JsDocToken.BANG) { return wrapNode(Token.BANG, parseBasicTypeExpression(next())); } else { Node basicTypeExpr = parseBasicTypeExpression(token); if (basicTypeExpr != null) { if (match(JsDocToken.QMARK)) { next(); return wrapNode(Token.QMARK, basicTypeExpr); } else if (match(JsDocToken.BANG)) { next(); return wrapNode(Token.BANG, basicTypeExpr); } } return basicTypeExpr; } } /** * BasicTypeExpression := '*' | 'null' | 'undefined' | TypeName * | FunctionType | UnionType | RecordType | ArrayType */ private Node parseBasicTypeExpression(JsDocToken token) { if (token == JsDocToken.STAR) { return newNode(Token.STAR); } else if (token == JsDocToken.LB) { skipEOLs(); return parseArrayType(next()); } else if (token == JsDocToken.LC) { skipEOLs(); return parseRecordType(next()); } else if (token == JsDocToken.LP) { skipEOLs(); return parseUnionType(next()); } else if (token == JsDocToken.STRING) { String string = stream.getString(); if ("function".equals(string)) { skipEOLs(); return parseFunctionType(next()); } else if ("null".equals(string) || "undefined".equals(string)) { return newStringNode(string); } else {

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> return parseTypeName(token); } } return reportGenericTypeSyntaxWarning(); } /** * TypeName := NameExpression | NameExpression TypeApplication * TypeApplication := '.<' TypeExpressionList '>' * TypeExpressionList := TypeExpression // a white lie */ private Node parseTypeName(JsDocToken token) { if (token != JsDocToken.STRING) { return reportGenericTypeSyntaxWarning(); } Node typeName = newStringNode(stream.getString()); if (match(JsDocToken.LT)) { next(); skipEOLs(); Node memberType = parseTypeExpressionList(next()); if (memberType != null) { typeName.addChildToFront(memberType); skipEOLs(); if (!match(JsDocToken.GT)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.gt"); } next(); } } return typeName; } /** * FunctionType := 'function' FunctionSignatureType * FunctionSignatureType := * TypeParameters '(' 'this' ':' TypeName, ParametersType ')' ResultType */ private Node parseFunctionType(JsDocToken token) { // NOTE(nicksantos): We're not implementing generics at the moment, so // just throw out TypeParameters. if (token != JsDocToken.LP) { return reportTypeSyntaxWarning("msg.jsdoc.missing.lp"); } Node functionType = newNode(Token.FUNCTION); Node parameters = null; skipEOLs(); if (!match(JsDocToken.RP)) { token = next(); boolean hasParams = true; if (token == JsDocToken.STRING && "this".equals(stream.getString())) { if (match(JsDocToken.COLON)) { next(); skipEOLs(); Node thisType = wrapNode(Token.THIS, parseTypeName(next())); if (thisType == null) { return null; } functionType.addChildToFront(thisType); } else { return reportTypeSyntaxWarning("msg.jsdoc.missing.colon"); } if (match(JsDocToken.COMMA)) { next(); skipEOLs(); token = next(); } else { hasParams = false; } } if (hasParams

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>) { parameters = parseParametersType(token); if (parameters == null) { return null; } } } if (parameters != null) { functionType.addChildToBack(parameters); } skipEOLs(); if (!match(JsDocToken.RP)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.rp"); } skipEOLs(); Node resultType = parseResultType(next()); if (resultType == null) { return null; } else { functionType.addChildToBack(resultType); } return functionType; } /** * ParametersType := RestParameterType | NonRestParametersType * | NonRestParametersType ',' RestParameterType * RestParameterType := '...' Identifier * NonRestParametersType := ParameterType ',' NonRestParametersType * | ParameterType * | OptionalParametersType * OptionalParametersType := OptionalParameterType * | OptionalParameterType, OptionalParametersType * OptionalParameterType := ParameterType= * ParameterType := TypeExpression | Identifier ':' TypeExpression */ // NOTE(nicksantos): The official ES4 grammar forces optional and rest // arguments to come after the required arguments. Our parser does not // enforce this. Instead we allow them anywhere in the function at parse-time, // and then warn about them during type resolution. // // In theory, it might be mathematically nicer to do the order-checking here. // But in practice, the order-checking for structural functions is exactly // the same as the order-checking for @param annotations. And the latter // has to happen during type resolution. Rather than duplicate the // order-checking in two places, we just do all of it in type resolution. private Node parseParametersType(JsDocToken token) { Node paramsType = newNode(Token.LP); boolean isVarArgs = false; Node paramType = null; if (token != JsDocToken.RP) { do { if (paramType != null) { // skip past the comma next(); skipEOLs(); token = next(); } if (token == JsDocToken.ELLIPSIS) { // In the latest ES4 proposal, there are no type constraints allowed // on variable arguments. We support the old

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> syntax for backwards // compatibility, but we should gradually tear it out. skipEOLs(); if (match(JsDocToken.RP)) { paramType = newNode(Token.ELLIPSIS); } else { skipEOLs(); if (!match(JsDocToken.LB)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.lb"); } next(); skipEOLs(); paramType = wrapNode(Token.ELLIPSIS, parseTypeExpression(next())); skipEOLs(); if (!match(JsDocToken.RB)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.rb"); } skipEOLs(); next(); } isVarArgs = true; } else { paramType = parseTypeExpression(token); if (match(JsDocToken.EQUALS)) { skipEOLs(); next(); paramType = wrapNode(Token.EQUALS, paramType); } } if (paramType == null) { return null; } paramsType.addChildToBack(paramType); if (isVarArgs) { break; } } while (match(JsDocToken.COMMA)); } if (isVarArgs && match(JsDocToken.COMMA)) { return reportTypeSyntaxWarning("msg.jsdoc.function.varargs"); } // The right paren will be checked by parseFunctionType return paramsType; } /** * ResultType := <empty> | ':' void | ':' TypeExpression */ private Node parseResultType(JsDocToken token) { skipEOLs(); if (!match(JsDocToken.COLON)) { return newNode(Token.EMPTY); } token = next(); skipEOLs(); if (match(JsDocToken.STRING) && "void".equals(stream.getString())) { next(); return newNode(Token.VOID); } else { return parseTypeExpression(next()); } } /** * UnionType := '(' TypeUnionList ')' * TypeUnionList := TypeExpression | TypeExpression '|' TypeUnionList * * We've removed the empty union type. */ private Node parseUnionType(JsDocToken token) { return parseUnionTypeWithAlternate(token

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>, null); } /** * Create a new union type, with an alternate that has already been * parsed. The alternate may be null. */ private Node parseUnionTypeWithAlternate(JsDocToken token, Node alternate) { Node union = newNode(Token.PIPE); if (alternate != null) { union.addChildToBack(alternate); } Node expr = null; do { if (expr != null) { skipEOLs(); token = next(); Preconditions.checkState( token == JsDocToken.PIPE || token == JsDocToken.COMMA); boolean isPipe = token == JsDocToken.PIPE; if (isPipe && match(JsDocToken.PIPE)) { // We support double pipes for backwards compatiblity. next(); } skipEOLs(); token = next(); } expr = parseTypeExpression(token); if (expr == null) { return null; } union.addChildToBack(expr); // We support commas for backwards compatiblity. } while (match(JsDocToken.PIPE, JsDocToken.COMMA)); if (alternate == null) { skipEOLs(); if (!match(JsDocToken.RP)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.rp"); } next(); } return union; } /** * ArrayType := '[' ElementTypeList ']' * ElementTypeList := <empty> | TypeExpression | '...' TypeExpression * | TypeExpression ',' ElementTypeList */ private Node parseArrayType(JsDocToken token) { Node array = newNode(Token.LB); Node arg = null; boolean hasVarArgs = false; do { if (arg != null) { next(); skipEOLs(); token = next(); } if (token == JsDocToken.ELLIPSIS) { arg = wrapNode(Token.ELLIPSIS, parseTypeExpression(next())); hasVarArgs = true; } else { arg = parseTypeExpression(token); } if (arg == null) { return null; } array.addChildToBack(arg); if (hasVarArgs) { break; } skipEOLs(); } while (match(

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>JsDocToken.COMMA)); if (!match(JsDocToken.RB)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.rb"); } next(); return array; } /** * RecordType := '{' FieldTypeList '}' */ private Node parseRecordType(JsDocToken token) { Node recordType = newNode(Token.LC); Node fieldTypeList = parseFieldTypeList(token); if (fieldTypeList == null) { return reportGenericTypeSyntaxWarning(); } skipEOLs(); if (!match(JsDocToken.RC)) { return reportTypeSyntaxWarning("msg.jsdoc.missing.rc"); } next(); recordType.addChildToBack(fieldTypeList); return recordType; } /** * FieldTypeList := FieldType | FieldType ',' FieldTypeList */ private Node parseFieldTypeList(JsDocToken token) { Node fieldTypeList = newNode(Token.LB); do { Node fieldType = parseFieldType(token); if (fieldType == null) { return null; } fieldTypeList.addChildToBack(fieldType); skipEOLs(); if (!match(JsDocToken.COMMA)) { break; } // Move to the comma token. next(); // Move to the token passed the comma. skipEOLs(); token = next(); } while (true); return fieldTypeList; } /** * FieldType := FieldName | FieldName ':' TypeExpression */ private Node parseFieldType(JsDocToken token) { Node fieldName = parseFieldName(token); if (fieldName == null) { return null; } skipEOLs(); if (!match(JsDocToken.COLON)) { return fieldName; } // Move to the colon. next(); // Move to the token after the colon and parse // the type expression. skipEOLs(); Node typeExpression = parseTypeExpression(next()); if (typeExpression == null) { return null; } Node fieldType = newNode(Token.COLON); fieldType.addChildToBack(fieldName); fieldType.addChildToBack(typeExpression); return fieldType; } /** * FieldName := NameExpression | StringLiteral | NumberLiteral | * ReservedIdentifier */ private Node parseFieldName(JsDoc

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>Token token) { switch (token) { case STRING: String string = stream.getString(); return newStringNode(string); default: return null; } } private Node wrapNode(int type, Node n) { return n == null ? null : new Node(type, n, stream.getLineno(), stream.getCharno()); } private Node newNode(int type) { return new Node(type, stream.getLineno(), stream.getCharno()); } private Node newStringNode(String s) { return Node.newString(s, stream.getLineno(), stream.getCharno()); } private Node reportTypeSyntaxWarning(String warning) { parser.addWarning(warning, stream.getLineno(), stream.getCharno()); return null; } private Node reportGenericTypeSyntaxWarning() { return reportTypeSyntaxWarning("msg.jsdoc.type.syntax"); } /** * Eats tokens until {@link JsDocToken#EOL} included, and switches back the * state to {@link State#SEARCHING_ANNOTATION}. */ private JsDocToken eatTokensUntilEOL() { return eatTokensUntilEOL(next()); } /** * Eats tokens until {@link JsDocToken#EOL} included, and switches back the * state to {@link State#SEARCHING_ANNOTATION}. */ private JsDocToken eatTokensUntilEOL(JsDocToken token) { do { if (token == JsDocToken.EOL || token == JsDocToken.EOC || token == JsDocToken.EOF) { state = State.SEARCHING_ANNOTATION; return token; } token = next(); } while (true); } /** * Specific value indicating that the {@link #unreadToken} contains no token. */ private static final JsDocToken NO_UNREAD_TOKEN = null; /** * One token buffer. */ private JsDocToken unreadToken = NO_UNREAD_TOKEN; /** * Tests whether the next symbol of the token stream matches the specific * token. */ private boolean match(JsDocToken token) { unreadToken = next(); return unreadToken == token; } /** * Tests that

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> the next symbol of the token stream matches one of the specified * tokens. */ private boolean match(JsDocToken token1, JsDocToken token2) { unreadToken = next(); return unreadToken == token1 || unreadToken == token2; } /** * Gets the next token of the token stream or the buffered token if a matching * was previously made. */ private JsDocToken next() { if (unreadToken == NO_UNREAD_TOKEN) { return stream.getJsDocToken(); } else { return current(); } } /** * Gets the current token, invalidating it in the process. */ private JsDocToken current() { JsDocToken t = unreadToken; unreadToken = NO_UNREAD_TOKEN; return t; } /** * Skips all EOLs and all empty lines in the JSDoc. Call this method if you * want the JSDoc entry to span multiple lines. */ private void skipEOLs() { while (match(JsDocToken.EOL)) { next(); if (match(JsDocToken.STAR)) { next(); } } } /** * Determines whether the parser has been populated with docinfo with a * fileoverview tag. */ private boolean hasParsedFileOverviewDocInfo() { return jsdocBuilder.isPopulatedWithFileOverview(); } boolean hasParsedJSDocInfo() { return jsdocBuilder.isPopulated(); } JSDocInfo retrieveAndResetParsedJSDocInfo() { return jsdocBuilder.build(sourceName); } /** * Gets the fileoverview JSDocInfo, if any. */ JSDocInfo getFileOverviewJSDocInfo() { return fileOverviewJSDocInfo; } }

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> if (CombinedLiveRangeChecker.shouldVisit(n)) { for (CombinedLiveRangeChecker callback : callbacks) { callback.visit(t, n, parent); } } } } /** * A simple wrapper calls to call two AbstractCfgNodeTraversalCallback * callback during the same traversal. Both traversals must have the same * "shouldTraverse" conditions. */ private static class CombinedLiveRangeChecker extends AbstractCfgNodeTraversalCallback { private final LiveRangeChecker callback1; private final LiveRangeChecker callback2; CombinedLiveRangeChecker( LiveRangeChecker callback1, LiveRangeChecker callback2) { this.callback1 = callback1; this.callback2 = callback2; } /** * @return Whether any CombinedLiveRangeChecker would be interested in the * node. */ public static boolean shouldVisit(Node n) { return LiveRangeChecker.shouldVisit(n); } @Override public void visit(NodeTraversal t, Node n, Node parent) { callback1.visit(t, n, parent); callback2.visit(t, n, parent); } void connectIfCrossed(UndiGraph<Var, Void> interferenceGraph) { if (callback1.crossed || callback2.crossed) { Var v1 = callback1.getDef(); Var v2 = callback2.getDef(); interferenceGraph.connectIfNotFound(v1, null, v2); } } } /** * Tries to remove variable declaration if the variable has been coalesced * with another variable that has already been declared. */ private void removeVarDeclaration(Node name) { Node var = name.getParent(); Node parent = var.getParent(); // Special case when we are in FOR-IN loop. if (NodeUtil.isForIn(parent)) { var.removeChild(name); parent.replaceChild(var, name); } else if (var.getChildCount() == 1) { // The removal is easy when there is only one variable in the VAR node. if (name.hasChildren()) { Node value = name.removeFirstChild(); var.removeChild(name); Node assign = new Node(Token

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>.ASSIGN, name, value); // We don't need to wrapped it with EXPR node if it is within a FOR. if (parent.getType() != Token.FOR) { assign = NodeUtil.newExpr(assign); } parent.replaceChild(var, assign); } else { // In a FOR( ; ; ) node, we must replace it with an EMPTY or else it // becomes a FOR-IN node. NodeUtil.removeChild(parent, var); } } else { if (!name.hasChildren()) { var.removeChild(name); } // We are going to leave duplicated declaration otherwise. } } private static class LiveRangeChecker extends AbstractCfgNodeTraversalCallback { boolean defFound = false; boolean crossed = false; private final Var def; private final Var use; public LiveRangeChecker(Var def, Var use) { this.def = def; this.use = use; } Var getDef() { return def; } /** * @return Whether any LiveRangeChecker would be interested in the node. */ public static boolean shouldVisit(Node n) { return (NodeUtil.isName(n) || (n.hasChildren() && NodeUtil.isName(n.getFirstChild()))); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (!defFound && isAssignTo(def, n, parent)) { defFound = true; } if (defFound && (use == null || isReadFrom(use, n))) { crossed = true; } } private static boolean isAssignTo(Var var, Node n, Node parent) { if (NodeUtil.isName(n) && var.getName().equals(n.getString()) && parent != null) { if (parent.getType() == Token.LP) { // In a function declaration, the formal parameters are assigned. return true; } else if (NodeUtil.isVar(parent)) { // If this is a VAR declaration, if the name node has a child, we are // assigning to that name. return n.hasChildren(); } return false; // Definitely a read. } else { // Last

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>ly, any assignmentOP is also an assign. Node name = n.getFirstChild(); return name != null && NodeUtil.isName(name) && var.getName().equals(name.getString()) && NodeUtil.isAssignmentOp(n); } } private static boolean isReadFrom(Var var, Node name) { return name != null && NodeUtil.isName(name) && var.getName().equals(name.getString()) && !NodeUtil.isLhs(name, name.getParent()); } } }

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> endLine(); } @Override void endCaseBody() { super.endCaseBody(); indent--; endStatement(); } @Override void appendOp(String op, boolean binOp) { if (binOp) { if (getLastChar() != ' ') { append(" "); } append(op); append(" "); } else { append(op); } } /** * If the body of a for loop or the then clause of an if statement has * a single statement, should it be wrapped in a block? * {@inheritDoc} */ @Override boolean shouldPreserveExtraBlocks() { // When pretty-printing, always place the statement in its own block // so it is printed on a separate line. This allows breakpoints to be // placed on the statement. return true; } /** * @return The TRY node for the specified CATCH node. */ private Node getTryForCatch(Node n) { return n.getParent().getParent(); } /** * @return Whether the a line break should be added after the specified * BLOCK. */ @Override boolean breakAfterBlockFor(Node n, boolean isStatementContext) { Preconditions.checkState(n.getType() == Token.BLOCK); Node parent = n.getParent(); if (parent != null) { int type = parent.getType(); switch (type) { case Token.DO: // Don't break before 'while' in DO-WHILE statements. return false; case Token.FUNCTION: // FUNCTIONs are handled separately, don't break here. return false; case Token.TRY: // Don't break before catch return n != parent.getFirstChild(); case Token.CATCH: // Don't break before finally return !NodeUtil.hasFinally(getTryForCatch(parent)); case Token.IF: // Don't break before else return n == parent.getLastChild(); } } return true; } } static class CompactCodePrinter extends MappedCodePrinter implements HasGetCode { // The CompactCodePrinter tries to emit just enough newlines to stop there // being lines longer than the threshold. Since the output is going to be // gz

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>: case Token.SHEQ: case Token.SHNE: case Token.CASE: Node left; Node right; if (operatorToken == Token.CASE) { left = condition.getParent().getFirstChild(); // the switch condition right = condition.getFirstChild(); } else { left = condition.getFirstChild(); right = condition.getLastChild(); } Node typeOfNode = null; Node stringNode = null; if (left.getType() == Token.TYPEOF && right.getType() == Token.STRING) { typeOfNode = left; stringNode = right; } else if (right.getType() == Token.TYPEOF && left.getType() == Token.STRING) { typeOfNode = right; stringNode = left; } if (typeOfNode != null && stringNode != null) { Node operandNode = typeOfNode.getFirstChild(); JSType operandType = getTypeIfRefinable(operandNode, blindScope); if (operandType != null) { boolean resultEqualsValue = operatorToken == Token.EQ || operatorToken == Token.SHEQ || operatorToken == Token.CASE; if (!outcome) { resultEqualsValue = !resultEqualsValue; } return caseTypeOf(operandNode, operandType, stringNode.getString(), resultEqualsValue, blindScope); } } } switch (operatorToken) { case Token.AND: if (outcome) { return caseAndOrNotShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, true); } else { return caseAndOrMaybeShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, true); } case Token.OR: if (!outcome) { return caseAndOrNotShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, false); } else { return caseAndOrMaybeShortCircuiting(condition.getFirstChild(), condition.getLastChild(), blindScope, false); } case Token.EQ: if (outcome) { return caseEquality(condition, blindScope, EQ); } else { return caseEquality(condition, blindScope, NE); } case Token.NE: if (outcome) { return caseEquality

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>(condition, blindScope, NE); } else { return caseEquality(condition, blindScope, EQ); } case Token.SHEQ: if (outcome) { return caseEquality(condition, blindScope, SHEQ); } else { return caseEquality(condition, blindScope, SHNE); } case Token.SHNE: if (outcome) { return caseEquality(condition, blindScope, SHNE); } else { return caseEquality(condition, blindScope, SHEQ); } case Token.NAME: case Token.GETPROP: return caseNameOrGetProp(condition, blindScope, outcome); case Token.ASSIGN: return firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild(), firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild().getNext(), blindScope, outcome), outcome); case Token.NOT: return firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild(), blindScope, !outcome); case Token.LE: case Token.LT: case Token.GE: case Token.GT: if (outcome) { return caseEquality(condition, blindScope, INEQ); } break; case Token.INSTANCEOF: return caseInstanceOf( condition.getFirstChild(), condition.getLastChild(), blindScope, outcome); case Token.IN: if (outcome && condition.getFirstChild().getType() == Token.STRING) { return caseIn(condition.getLastChild(), condition.getFirstChild().getString(), blindScope); } break; case Token.CASE: Node left = condition.getParent().getFirstChild(); // the switch condition Node right = condition.getFirstChild(); if (outcome) { return caseEquality(left, right, blindScope, SHEQ); } else { return caseEquality(left, right, blindScope, SHNE); } } return nextPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome); } private FlowScope caseEquality(Node condition, FlowScope blindScope, Function<TypePair, TypePair> merging) { return caseEquality(condition.getFirstChild(), condition.getLastChild(), blindScope, merging); } private FlowScope caseEquality

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>compiler)); } } /** * Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code. */ private void normalizeNodeTypes(Node n) { if (n.getType() == Token.EXPR_VOID) { n.setType(Token.EXPR_RESULT); reportChange(); } // Remove unused properties to minimize differences between ASTs // produced by the two parsers. if (n.getType() == Token.FUNCTION) { Preconditions.checkState(n.getProp(Node.FUNCTION_PROP) == null); } normalizeBlocks(n); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { // This pass is run during the CompilerTestCase validation, so this // parent pointer check serves as a more general check. Preconditions.checkState(child.getParent() == n); normalizeNodeTypes(child); } } /** * Add blocks to IF, WHILE, DO, etc. */ private void normalizeBlocks(Node n) { if (NodeUtil.isControlStructure(n) && n.getType() != Token.LABEL && n.getType() != Token.SWITCH) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (NodeUtil.isControlStructureCodeBlock(n,c) && c.getType() != Token.BLOCK) { Node newBlock = new Node(Token.BLOCK, n.getLineno(), n.getCharno()); newBlock.copyInformationFrom(n); n.replaceChild(c, newBlock); if (c.getType() != Token.EMPTY) { newBlock.addChildrenToFront(c); } else { newBlock.setWasEmptyNode(true); } c = newBlock; reportChange(); } } } } /** * Normalize where annotations appear on the AST. Copies * around existing JSDoc annotations as well as internal annotations. */ static class PrepareAnnotations extends NodeTraversal.AbstractPostOrderCallback { private final CodingConvention convention; PrepareAnnotations(AbstractCompiler compiler) { this.convention = compiler.getCodingConvention(); } /** * * In the AST that R

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>hino gives us, it needs to make a distinction * between jsdoc on the object literal node and jsdoc on the object literal * value. For example, * <pre> * var x = { * / JSDOC / * a: 'b', * c: / JSDOC / 'd' * }; * </pre> * * But in few narrow cases (in particular, function literals), it's * a lot easier for us if the doc is attached to the value. */ public void visit(NodeTraversal t, Node n, Node parent) { int nType = n.getType(); switch (nType) { case Token.NAME: case Token.STRING: String nString = n.getString(); if (nType == Token.NAME && n.getParent().getType() == Token.CALL && "eval".equals(nString)) { n.putBooleanProp(Node.DIRECT_EVAL, true); } if (convention.isConstant(nString)) { n.putBooleanProp(Node.IS_CONSTANT_NAME, true); } break; case Token.FUNCTION: JSDocInfo fnInfo = n.getJSDocInfo(); if (fnInfo == null) { // Look for the info on other nodes. if (parent.getType() == Token.ASSIGN) { // on ASSIGNs fnInfo = parent.getJSDocInfo(); } else if (parent.getType() == Token.NAME) { // on var NAME = function() { ... }; fnInfo = parent.getParent().getJSDocInfo(); } } // Compute which function parameters are optional and // which are var_args. Node args = n.getFirstChild().getNext(); for (Node arg = args.getFirstChild(); arg != null; arg = arg.getNext()) { String argName = arg.getString(); JSTypeExpression typeExpr = fnInfo == null ? null : fnInfo.getParameterType(argName); if (convention.isOptionalParameter(arg) || typeExpr != null && typeExpr.isOptionalArg()) { arg.putBooleanProp(Node.IS_OPTIONAL_PARAM, true); } if (convention.isVarArgsParameter

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>(arg) || typeExpr != null && typeExpr.isVarArgs()) { arg.putBooleanProp(Node.IS_VAR_ARGS_PARAM, true); } } break; case Token.OBJECTLIT: if (n.getType() == Token.OBJECTLIT) { for (Node key = n.getFirstChild(); key != null; key = key.getNext().getNext()) { Node value = key.getNext(); if (key.getJSDocInfo() != null && key.getNext().getType() == Token.FUNCTION) { value.setJSDocInfo(key.getJSDocInfo()); } } } break; } // TODO(johnlenz): Determine if it is possible to simply use the javadoc // everywhere rather than use IS_DISPATCHER. /* * Translate dispatcher info into the property expected node. */ if (n.getJSDocInfo() != null && n.getJSDocInfo().isJavaDispatch()) { if (n.getType() == Token.ASSIGN) { Node fnNode = n.getLastChild(); Preconditions.checkState(fnNode.getType() == Token.FUNCTION); fnNode.putBooleanProp(Node.IS_DISPATCHER, true); } } } } }

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> If the expectation is not met, issue a warning at the provided * node's source code position. */ void expectStringOrNumber( NodeTraversal t, Node n, JSType type, String msg) { if (!type.matchesNumberContext() && !type.matchesStringContext()) { mismatch(t, n, msg, type, NUMBER_STRING); } } /** * Expect the type to be anything but the void type. If the expectation is not * met, issue a warning at the provided node's source code position. Note that * a union type that includes the void type and at least one other type meets * the expectation. * @return Whether the expectation was met. */ boolean expectNotVoid( NodeTraversal t, Node n, JSType type, String msg, JSType expectedType) { if (type.isVoidType()) { mismatch(t, n, msg, type, expectedType); return false; } return true; } /** * Expect that the type of a switch condition matches the type of its * case condition. */ void expectSwitchMatchesCase(NodeTraversal t, Node n, JSType switchType, JSType caseType) { // ECMA-262, page 68, step 3 of evaluation of CaseBlock, // but allowing extra autoboxing. // TODO(user): remove extra conditions when type annotations // in the code base have adapted to the change in the compiler. if (!switchType.canTestForShallowEqualityWith(caseType) && (caseType.autoboxesTo() == null || !caseType.autoboxesTo().isSubtype(switchType))) { mismatch(t, n.getFirstChild(), "case expression doesn't match switch", caseType, switchType); } } /** * Expect that the first type can be addressed with GETELEM syntax, * and that the second type is the right type for an index into the * first type. * * @param t The node traversal. * @param n The node to issue warnings on. * @param objType The type of the left side of the GETELEM. * @param indexType The type inside the brackets of the GETELEM. */ void expectIndexMatch(NodeTraversal t

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> assigned to a symbol of the second * type. * * @param t The node traversal. * @param n The node to issue warnings on. * @param rightType The type on the RHS of the assign. * @param leftType The type of the symbol on the LHS of the assign. * @param msg An extra message for the mismatch warning, if necessary. * @return True if the types matched, false otherwise. */ boolean expectCanAssignTo(NodeTraversal t, Node n, JSType rightType, JSType leftType, String msg) { if (!rightType.canAssignTo(leftType)) { if (bothIntrinsics(rightType, leftType)) { // We have a superior warning for this mistake, which gives you // the line numbers of both types. registerMismatch(rightType, leftType); } else { mismatch(t, n, msg, rightType, leftType); } return false; } return true; } private boolean bothIntrinsics(JSType rightType, JSType leftType) { return (leftType.isConstructor() || leftType.isEnumType()) && (rightType.isConstructor() || rightType.isEnumType()); } /** * Expect that the type of an argument matches the type of the parameter * that it's fulfilling. * * @param t The node traversal. * @param n The node to issue warnings on. * @param argType The type of the argument. * @param paramType The type of the parameter. * @param callNode The call node, to help with the warning message. * @param ordinal The argument ordinal, to help with the warning message. */ void expectArgumentMatchesParameter(NodeTraversal t, Node n, JSType argType, JSType paramType, Node callNode, int ordinal) { if (!argType.canAssignTo(paramType)) { mismatch(t, n, String.format("actual parameter %d of %s does not match " + "formal parameter", ordinal, getReadableJSTypeName(callNode.getFirstChild(), false)), argType, paramType); } } /** * Expect that the first type can override a property of the second * type. *

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> native type. if (var.input == null) { n.setJSType(varType); if (parent.getType() == Token.VAR) { if (n.getFirstChild() != null) { n.getFirstChild().setJSType(varType); } } else { Preconditions.checkState(parent.getType() == Token.FUNCTION); parent.setJSType(varType); } } else { // Always warn about duplicates if the overridden type does not // match the original type. // // If the types match, suppress the warning iff there was a @suppress // tag, or if the original declaration was a stub. if (!(allowDupe || var.getParentNode().getType() == Token.EXPR_RESULT) || !newType.equals(varType)) { compiler.report( JSError.make(sourceName, n, DUP_VAR_DECLARATION, variableName, newType.toString(), var.getInputName(), String.valueOf(var.nameNode.getLineno()), varType.toString())); } } } } /** * Expect that all properties on interfaces that this type implements are * implemented. */ void expectAllInterfacePropertiesImplemented(FunctionType type) { ObjectType instance = type.getInstanceType(); for (ObjectType implemented : type.getAllImplementedInterfaces()) { if (implemented.getImplicitPrototype() != null) { for (String prop : implemented.getImplicitPrototype().getOwnPropertyNames()) { if (!instance.hasProperty(prop)) { Node source = type.getSource(); Preconditions.checkNotNull(source); String sourceName = (String) source.getProp(Node.SOURCENAME_PROP); sourceName = sourceName == null ? "" : sourceName; compiler.report(JSError.make(sourceName, source, INTERFACE_METHOD_NOT_IMPLEMENTED, prop, implemented.toString(), instance.toString())); registerMismatch(instance, implemented); } } } } } /** * Report a type mismatch */ private void mismatch(NodeTraversal t, Node n, String msg, JSType found, JSType required) { mismatch(t.getSourceName(), n, msg, found, required); } private void mismatch(Node

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>-readable * name "Foo.prototype.bar". * * @param n The node. * @param dereference If true, the type of the node will be dereferenced * to an Object type, if possible. */ String getReadableJSTypeName(Node n, boolean dereference) { // If we're analyzing a GETPROP, the property may be inherited by the // prototype chain. So climb the prototype chain and find out where // the property was originally defined. if (n.getType() == Token.GETPROP) { ObjectType objectType = getJSType(n.getFirstChild()).dereference(); if (objectType != null) { String propName = n.getLastChild().getString(); while (objectType != null && !objectType.hasOwnProperty(propName)) { objectType = objectType.getImplicitPrototype(); } // Don't show complex function names or anonymous types. // Instead, try to get a human-readable type name. if (objectType != null && (objectType.getConstructor() != null || objectType.isFunctionPrototypeType())) { return objectType.toString() + "." + propName; } } } JSType type = getJSType(n); if (dereference) { ObjectType dereferenced = type.dereference(); if (dereferenced != null) { type = dereferenced; } } String qualifiedName = n.getQualifiedName(); if (type.isFunctionPrototypeType() || (type.toObjectType() != null && type.toObjectType().getConstructor() != null)) { return type.toString(); } else if (qualifiedName != null) { return qualifiedName; } else if (type instanceof FunctionType) { // Don't show complex function names. return "function"; } else { return type.toString(); } } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { // TODO(user): This branch indicates a compiler bug, not worthy of // halting the compilation but we should log this and analyze to

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>/* * Copyright 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp.parsing; import com.google.common.base.Preconditions; import com.google.javascript.jscomp.mozilla.rhino.ScriptRuntime; /** * This class implements the scanner for JsDoc strings. * * It is heavily based on Rhino's TokenStream. * * */ class JsDocTokenStream { /* * For chars - because we need something out-of-range * to check. (And checking EOF by exception is annoying.) * Note distinction from EOF token type! */ private final static int EOF_CHAR = -1; JsDocTokenStream(String sourceString) { this(sourceString, 0); } JsDocTokenStream(String sourceString, int lineno) { this(sourceString, lineno, 0); } JsDocTokenStream(String sourceString, int lineno, int initCharno) { Preconditions.checkNotNull(sourceString); this.lineno = lineno; this.sourceString = sourceString; this.sourceEnd = sourceString.length(); this.sourceCursor = this.cursor = 0; this.initLineno = lineno; this.initCharno = initCharno; } /** * Tokenizes JSDoc comments. */ @SuppressWarnings("fallthrough") final JsDocToken getJsDocToken() { int c; stringBufferTop = 0; for (;;) { // eat white spaces for (;;) { charno = -1; c = getChar(); if (c == EOF_CHAR) { return

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> JsDocToken.EOF; } else if (c == '\n') { return JsDocToken.EOL; } else if (!isJSSpace(c)) { break; } } switch (c) { // annotation, e.g. @type or @constructor case '@': do { c = getChar(); if (isAlpha(c)) { addToString(c); } else { ungetChar(c); this.string = getStringFromBuffer(); stringBufferTop = 0; return JsDocToken.ANNOTATION; } } while (true); case '*': if (matchChar('/')) { return JsDocToken.EOC; } else { return JsDocToken.STAR; } case ',': return JsDocToken.COMMA; case '>': return JsDocToken.GT; case '(': return JsDocToken.LP; case ')': return JsDocToken.RP; case '{': return JsDocToken.LC; case '}': return JsDocToken.RC; case '[': return JsDocToken.LB; case ']': return JsDocToken.RB; case '?': return JsDocToken.QMARK; case '!': return JsDocToken.BANG; case ':': return JsDocToken.COLON; case '=': return JsDocToken.EQUALS; case '|': matchChar('|'); return JsDocToken.PIPE; case '.': c = getChar(); if (c == '<') { return JsDocToken.LT; } else { if (c == '.') { c = getChar(); if (c == '.') { return JsDocToken.ELLIPSIS; } else { addToString('.'); } } // we may backtrack across line boundary ungetBuffer[ungetCursor++] = c; c = '.'; } // fall through default: { // recognize a jsdoc string but discard last . if it is followed by // a non-jsdoc comment char, e.g. Array.< int c1 = c; addToString(c); int c2 = getChar(); if (!isJSDocString(

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>c2)) { ungetChar(c2); this.string = getStringFromBuffer(); stringBufferTop = 0; return JsDocToken.STRING; } else { do { c1 = c2; c2 = getChar(); if (c1 == '.' && c2 == '<') { ungetChar(c2); ungetChar(c1); this.string = getStringFromBuffer(); stringBufferTop = 0; return JsDocToken.STRING; } else { if (isJSDocString(c2)) { addToString(c1); } else { ungetChar(c2); addToString(c1); this.string = getStringFromBuffer(); stringBufferTop = 0; return JsDocToken.STRING; } } } while (true); } } } } } /** * Gets the remaining JSDoc line without the {@link JsDocToken#EOL}, * {@link JsDocToken#EOF} or {@link JsDocToken#EOC}. */ @SuppressWarnings("fallthrough") String getRemainingJSDocLine() { int c; for (;;) { c = getChar(); switch (c) { case '*': if (peekChar() != '/') { addToString(c); break; } // fall through case EOF_CHAR: case '\n': ungetChar(c); this.string = getStringFromBuffer(); stringBufferTop = 0; return this.string; default: addToString(c); break; } } } final int getLineno() { return lineno; } final int getCharno() { return lineno == initLineno? initCharno + charno : charno; } final String getString() { return string; } final boolean eof() { return hitEOF; } private String getStringFromBuffer() { tokenEnd = cursor; return new String(stringBuffer, 0, stringBufferTop); } private void addToString(int c) { int N = stringBufferTop; if (N == stringBuffer.length) { char[] tmp = new char[stringBuffer.length * 2]; System.arraycopy

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>Later == n) { lvalueToRemoveLater = null; if (n.getType() == Token.ASSIGN) { Node last = n.getLastChild(); n.removeChild(last); parent.replaceChild(n, last); } else { Preconditions.checkState(n.getType() == Token.NAME); n.removeChild(n.getFirstChild()); } compiler.reportCodeChange(); } if (n.getType() == Token.CALL) { if (t.inGlobalScope()) { // If there's a function call in the global scope, // we just say it's unsafe and freeze all the defines. // // NOTE(nicksantos): We could be a lot smarter here. For example, // ReplaceOverriddenVars keeps a call graph of all functions and // which functions/variables that they reference, and tries // to statically determine which functions are "safe" and which // are not. But this would be overkill, expecially because // the intended use of defines is with config_files, where // all the defines are at the top of the bundle. for (DefineInfo info : assignableDefines.values()) { setDefineInfoNotAssignable(info, t); } assignableDefines.clear(); } } updateAssignAllowedStack(n, false); } /** * Determines whether assignment to a define should be allowed * in the subtree of the given node, and if not, records that fact. * * @param n The node whose subtree we're about to enter or exit. * @param entering True if we're entering the subtree, false otherwise. */ private void updateAssignAllowedStack(Node n, boolean entering) { switch (n.getType()) { case Token.CASE: case Token.FOR: case Token.FUNCTION: case Token.HOOK: case Token.IF: case Token.SWITCH: case Token.WHILE: if (entering) { assignAllowed.push(0); } else { assignAllowed.remove(); } break; } } /** * Determines whether assignment to a define should be allowed * at the current point of the traversal. */ private boolean isAssignAllowed() { return assignAllowed.element() == 1;

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> visit all nodes but not traverse into function * bodies. */ public abstract static class AbstractShallowCallback implements Callback { public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { // We do want to traverse the name of a named function, but we don't // want to traverse the arguments or body. return parent == null || parent.getType() != Token.FUNCTION || n == parent.getFirstChild(); } } /** * Abstract callback to visit all structure and statement nodes but doesn't * traverse into functions or expressions. */ public abstract static class AbstractShallowStatementCallback implements Callback { public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return parent == null || NodeUtil.isControlStructure(parent) || NodeUtil.isStatementBlock(parent); } } /** * Abstract callback to visit a pruned set of nodes. * */ public abstract static class AbstractNodeTypePruningCallback implements Callback { private final Set<Integer> nodeTypes; private final boolean include; /** * Creates an abstract pruned callback. * @param nodeTypes the nodes to include in the traversal */ public AbstractNodeTypePruningCallback(Set<Integer> nodeTypes) { this(nodeTypes, true); } /** * Creates an abstract pruned callback. * @param nodeTypes the nodes to include/exclude in the traversal * @param include whether to include or exclude the nodes in the traversal */ public AbstractNodeTypePruningCallback(Set<Integer> nodeTypes, boolean include) { this.nodeTypes = nodeTypes; this.include = include; } public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return include == nodeTypes.contains(n.getType()); } } /** * Creates a node traversal using the specified callback interface. */ public NodeTraversal(AbstractCompiler compiler, Callback cb) { this(compiler, cb, new SyntacticScopeCreator(compiler)); } /** * Creates a node traversal using the specified callback interface * and the scope creator. */ public NodeTraversal(AbstractCompiler compiler, Callback cb, ScopeCreator scopeCreator) { this.callback = cb;

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>); } } private static final String MISSING_SOURCE = "[source unknown]"; private String formatNodePosition(Node n) { if (n == null) { return MISSING_SOURCE + "\n"; } int lineNumber = n.getLineno(); int columnNumber = n.getCharno(); String src = compiler.getSourceLine(sourceName, lineNumber); if (src == null) { src = MISSING_SOURCE; } return sourceName + ":" + lineNumber + ":" + columnNumber + "\n" + src + "\n"; } /** * Traverses a parse tree recursively with a scope, starting with the given * root. This should only be used in the global scope. Otherwise, use * {@link #traverseAtScope}. */ void traverseWithScope(Node root, Scope s) { Preconditions.checkState(s.isGlobal()); sourceName = ""; curNode = root; pushScope(s); traverseBranch(root, null); popScope(); } /** * Traverses a parse tree recursively with a scope, starting at that scope's * root. */ void traverseAtScope(Scope s) { Node n = s.getRootNode(); if (n.getType() == Token.FUNCTION) { // We need to do some extra magic to make sure that the scope doesn't // get re-created when we dive into the function. sourceName = getSourceName(n); curNode = n; pushScope(s); Node args = n.getFirstChild().getNext(); Node body = args.getNext(); traverseBranch(args, n); traverseBranch(body, n); popScope(); } else { traverseWithScope(n, s); } } /** * Traverses an inner node recursively with a refined scope. An inner node may * be any node with a non {@code null} parent (i.e. all nodes except the * root). * * @param node the node to traverse * @param parent the node's parent, it may be not be {@code null} * @param refinedScope the refined scope of the scope currently at the top of * the scope stack or in trivial cases that very scope or {@

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>fallthrough") private void traverseBranch(Node n, Node parent) { int type = n.getType(); if (type == Token.SCRIPT) { sourceName = getSourceName(n); } curNode = n; if (!callback.shouldTraverse(this, n, parent)) return; switch (type) { case Token.CATCH: Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.getFirstChild().getType() == Token.NAME); // the first child is the catch var and the third child // is the code block traverseBranch(n.getFirstChild(), n); traverseBranch(n.getFirstChild().getNext().getNext(), n); break; case Token.FUNCTION: traverseFunction(n, parent); break; default: for (Node child = n.getFirstChild(); child != null; ) { // child could be replaced, in which case our child node // would no longer point to the true next Node next = child.getNext(); traverseBranch(child, n); child = next; } break; } curNode = n; callback.visit(this, n, parent); } /** * Traverses a function. */ private void traverseFunction(Node n, Node parent) { Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.getType() == Token.FUNCTION); final Node fnName = n.getFirstChild(); boolean anonymous = parent != null && NodeUtil.isFunctionAnonymous(n); if (!anonymous) { // Named functions are parent of the containing scope. traverseBranch(fnName, n); } curNode = n; pushScope(n); if (anonymous) { // Anonymous function names are parent of the contained scope. traverseBranch(fnName, n); } final Node args = fnName.getNext(); final Node body = args.getNext(); // Args traverseBranch(args, n); // Body Preconditions.checkState(body.getNext() == null && body.getType() == Token.BLOCK); traverseBranch(body, n); popScope(); } /** Examines the functions stack for the last instance of a function node. */

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> @SuppressWarnings("unchecked") public Node getEnclosingFunction() { if (scopes.size() + scopeRoots.size() < 2) { return null; } else { if (scopeRoots.isEmpty()) { return scopes.peek().getRootNode(); } else { return scopeRoots.peek(); } } } /** Creates a new scope (e.g. when entering a function). */ private void pushScope(Node node) { Preconditions.checkState(curNode != null); scopeRoots.push(node); cfgs.push(null); if (scopeCallback != null) { scopeCallback.enterScope(this); } } /** Creates a new scope (e.g. when entering a function). */ private void pushScope(Scope s) { Preconditions.checkState(curNode != null); scopes.push(s); cfgs.push(null); if (scopeCallback != null) { scopeCallback.enterScope(this); } } /** Pops back to the previous scope (e.g. when leaving a function). */ private void popScope() { if (scopeCallback != null) { scopeCallback.exitScope(this); } if (scopeRoots.isEmpty()) { scopes.pop(); } else { scopeRoots.pop(); } cfgs.pop(); } /** Gets the current scope. */ public Scope getScope() { Scope scope = scopes.isEmpty() ? null : scopes.peek(); if (scopeRoots.isEmpty()) { return scope; } Iterator<Node> it = scopeRoots.descendingIterator(); while (it.hasNext()) { scope = scopeCreator.createScope(it.next(), scope); scopes.push(scope); } scopeRoots.clear(); return scope; } /** Gets the control flow graph for the current JS scope. */ public ControlFlowGraph<Node> getControlFlowGraph() { if (cfgs.peek() == null) { ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false); cfa.process(null, getScopeRoot()); cfgs.pop(); cfgs.push(cfa.getCfg()); } return cfgs.peek(); } /** Returns

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> the current scope's root. */ public Node getScopeRoot() { if (scopeRoots.isEmpty()) { return scopes.peek().getRootNode(); } else { return scopeRoots.peek(); } } /** * Determines whether the traversal is currently in the global scope. */ boolean inGlobalScope() { return getScopeDepth() <= 1; } int getScopeDepth() { return scopes.size() + scopeRoots.size(); } public boolean hasScope() { return !(scopes.isEmpty() && scopeRoots.isEmpty()); } /** Reports a diagnostic (error or warning) */ public void report(Node n, DiagnosticType diagnosticType, String... arguments) { JSError error = JSError.make(getSourceName(), n, diagnosticType, arguments); compiler.report(error); } private static String getSourceName(Node n) { String name = (String) n.getProp(Node.SOURCENAME_PROP); return name == null ? "" : name; } /** * Creates a JSError during NodeTraversal. * * @param n Determines the line and char position within the source file name * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public JSError makeError(Node n, CheckLevel level, DiagnosticType type, String... arguments) { return JSError.make(getSourceName(), n.getLineno(), n.getCharno(), level, type, arguments); } /** * Creates a JSError during NodeTraversal. * * @param n Determines the line and char position within the source file name * @param type The DiagnosticType * @param arguments Arguments to be incorporated into the message */ public JSError makeError(Node n, DiagnosticType type, String... arguments) { return JSError.make(getSourceName(), n, type, arguments); } }

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>/* * Copyright 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp.parsing; /** * JSDoc-specific tokens. * * This class is based on Rhino's Token. * * */ enum JsDocToken { // Tokens recycled from Rhino EOF, // end of file token - (not EOF_CHAR) EOL, // end of line LT, GT, STRING, LB, // left and right brackets RB, LC, // left and right curlies (braces) RC, LP, // left and right parentheses RP, COMMA, // comma operator COLON, // JsDoc-only tokens ANNOTATION, PIPE, STAR, EOC, QMARK, ELLIPSIS, BANG, EQUALS; }

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>modules.length]; for (int i = 0; i < modules.length; i++) { expected[i] = ""; for (CompilerInput input : modules[i].getInputs()) { expected[i] += input.getSourceFile().getCode(); } } test(modules, expected, null, warning); } catch (IOException e) { throw new RuntimeException(e); } } /** * Verifies that the compiler pass's JS output matches the expected output * and (optionally) that an expected warning is issued. Or, if an error is * expected, this method just verifies that the error is encountered. * * @param compiler A compiler that has been initialized via * {@link Compiler#init} * @param expected Expected output, or null if an error is expected * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected */ protected void test(Compiler compiler, String[] expected, DiagnosticType error, DiagnosticType warning) { test(compiler, expected, error, warning, null); } /** * Verifies that the compiler pass's JS output matches the expected output * and (optionally) that an expected warning is issued. Or, if an error is * expected, this method just verifies that the error is encountered. * * @param compiler A compiler that has been initialized via * {@link Compiler#init} * @param expected Expected output, or null if an error is expected * @param error Expected error, or null if no error is expected * @param warning Expected warning, or null if no warning is expected * @param description The description of the expected warning, * or null if no warning is expected or if the warning's description * should not be examined */ private void test(Compiler compiler, String[] expected, DiagnosticType error, DiagnosticType warning, String description) { RecentChange recentChange = new RecentChange(); compiler.addChangeHandler(recentChange); Node root = compiler.parseInputs(); assertTrue("Unexpected parse error(s): " + Joiner.on("\n").join(compiler.getErrors()), root != null); Node externsRoot = root.getFirstChild(); Node mainRoot = root.getLastChild

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>(); // Save the tree for later comparison. Node rootClone = root.cloneTree(); Node externsRootClone = rootClone.getFirstChild(); Node mainRootClone = rootClone.getLastChild(); int numRepetitions = getNumRepetitions(); ErrorManager[] errorManagers = new ErrorManager[numRepetitions]; int aggregateWarningCount = 0; List<JSError> aggregateWarnings = Lists.newArrayList(); boolean hasCodeChanged = false; assertFalse("Code should not change before processing", recentChange.hasCodeChanged()); for (int i = 0; i < numRepetitions; ++i) { if (compiler.getErrorCount() == 0) { errorManagers[i] = new BlackHoleErrorManager(compiler); // Only run the type checking pass once, if asked. // Running it twice can cause unpredictable behavior because duplicate // objects for the same type are created, and the type system // uses reference equality to compare many types. if (typeCheckEnabled && i == 0) { TypeCheck check = createTypeCheck(compiler, typeCheckLevel); check.processForTesting(externsRoot, mainRoot); } // Only run the normalize pass once, if asked. if (normalizeEnabled && i == 0) { Normalize normalize = new Normalize(compiler, false); normalize.process(externsRoot, mainRoot); compiler.setNormalized(); } if (markNoSideEffects && i == 0) { MarkNoSideEffectCalls mark = new MarkNoSideEffectCalls(compiler); mark.process(externsRoot, mainRoot); } recentChange.reset(); getProcessor(compiler).process(externsRoot, mainRoot); if (checkLineNumbers) { (new LineNumberCheck(compiler)).process(externsRoot, mainRoot); } hasCodeChanged = hasCodeChanged || recentChange.hasCodeChanged(); aggregateWarningCount += errorManagers[i].getWarningCount(); aggregateWarnings.addAll(Lists.newArrayList(compiler.getWarnings())); if (normalizeEnabled) { boolean verifyDeclaredConstants = true; new Normalize.VerifyConstants(compiler, verifyDeclaredConstants) .process(externsRoot, mainRoot); } } } if (error == null) {

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> "compiler.reportCodeChange() was called " + "even though nothing changed", hasCodeChanged); } else { assertTrue("compiler.reportCodeChange() should have been called", hasCodeChanged); } if (compareAsTree) { String explanation = expectedRoot.checkTreeEquals(mainRoot); assertNull("\nExpected: " + compiler.toSource(expectedRoot) + "\nResult: " + compiler.toSource(mainRoot) + "\n" + explanation, explanation); } else if (expected != null) { assertEquals( Joiner.on("").join(expected), compiler.toSource(mainRoot)); } // Verify normalization is not invalidated. Node normalizeCheckRootClone = root.cloneTree(); Node normalizeCheckExternsRootClone = root.getFirstChild(); Node normalizeCheckMainRootClone = root.getLastChild(); new PrepareAst(compiler).process( normalizeCheckExternsRootClone, normalizeCheckMainRootClone); String explanation = normalizeCheckMainRootClone.checkTreeEquals(mainRoot); assertNull("Node structure normalization invalidated.\nExpected: " + compiler.toSource(normalizeCheckMainRootClone) + "\nResult: " + compiler.toSource(mainRoot) + "\n" + explanation, explanation); // TODO(johnlenz): enable this for most test cases. // Currently, this invalidates test for while-loops, for-loop // initializers, and other naming. However, a set of code // (FoldConstants, etc) runs before the Normalize pass, so this can't be // force on everywhere. if (normalizeEnabled) { new Normalize(compiler, true).process( normalizeCheckExternsRootClone, normalizeCheckMainRootClone); explanation = normalizeCheckMainRootClone.checkTreeEquals(mainRoot); assertNull("Normalization invalidated.\nExpected: " + compiler.toSource(normalizeCheckMainRootClone) + "\nResult: " + compiler.toSource(mainRoot) + "\n" + explanation, explanation); } } else { String errors = ""; for (JSError actualError : compiler.getErrors()) { errors += actualError.description + "\n"; } assertEquals("There should be one error. "

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> + errors, 1, compiler.getErrorCount()); assertEquals(errors, error, compiler.getErrors()[0].getType()); } } /** * Parses expected js inputs and returns the root of the parse tree. */ private Node parseExpectedJs(String[] expected) { Compiler compiler = createCompiler(); JSSourceFile[] inputs = new JSSourceFile[expected.length]; for (int i = 0; i < expected.length; i++) { inputs[i] = JSSourceFile.fromCode("expected" + i, expected[i]); } compiler.init(externsInputs, inputs, getOptions()); Node root = compiler.parseInputs(); assertTrue("Unexpected parse error(s): " + Joiner.on("\n").join(compiler.getErrors()), root != null); Node externsRoot = root.getFirstChild(); Node mainRoot = externsRoot.getNext(); // Only run the normalize pass, if asked. if (normalizeEnabled && normalizeExpected && !compiler.hasErrors()) { Normalize normalize = new Normalize(compiler, false); normalize.process(externsRoot, mainRoot); compiler.setNormalized(); } return mainRoot; } Node parseExpectedJs(String expected) { return parseExpectedJs(new String[] {expected}); } /** * Generates a list of modules from a list of inputs, such that each module * depends on the module before it. */ static JSModule[] createModuleChain(String... inputs) { JSModule[] modules = createModules(inputs); for (int i = 1; i < modules.length; i++) { modules[i].addDependency(modules[i - 1]); } return modules; } /** * Generates a list of modules from a list of inputs, such that each module * depends on the first module. */ static JSModule[] createModuleStar(String... inputs) { JSModule[] modules = createModules(inputs); for (int i = 1; i < modules.length; i++) { modules[i].addDependency(modules[0]); } return modules; } /** * Generates a list of modules from a list of inputs. Does not generate any * dependencies between the modules.

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> case Token.WHILE: case Token.DO: case Token.IF: computeGenKill(NodeUtil.getConditionExpression(n), gen, kill, conditional); return; case Token.FOR: if (!NodeUtil.isForIn(n)) { computeGenKill(NodeUtil.getConditionExpression(n), gen, kill, conditional); } else { // for(x in y) {...} Node lhs = n.getFirstChild(); Node rhs = lhs.getNext(); if (NodeUtil.isVar(lhs)) { // for(var x in y) {...} lhs = lhs.getLastChild(); } addToSetIfLocal(lhs, kill); addToSetIfLocal(lhs, gen); computeGenKill(rhs, gen, kill, conditional); } return; case Token.VAR: for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (c.hasChildren()) { computeGenKill(c.getFirstChild(), gen, kill, conditional); if (!conditional) { addToSetIfLocal(c, kill); } } } return; case Token.AND: case Token.OR: computeGenKill(n.getFirstChild(), gen, kill, conditional); // May short circuit. computeGenKill(n.getLastChild(), gen, kill, true); return; case Token.HOOK: computeGenKill(n.getFirstChild(), gen, kill, conditional); // Assume both sides are conditional. computeGenKill(n.getFirstChild().getNext(), gen, kill, true); computeGenKill(n.getLastChild(), gen, kill, true); return; case Token.NAME: if (isArgumentsName(n)) { markAllParametersEscaped(); } else { addToSetIfLocal(n, gen); } return; default: if (NodeUtil.isAssignmentOp(n) && NodeUtil.isName(n.getFirstChild())) { Node lhs = n.getFirstChild(); if (!conditional) { addToSetIfLocal(lhs, kill); } if (!NodeUtil.isAssign(n)) { // assignments such as a += 1 reads a. addToSetIfLocal(

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>lhs, gen); } computeGenKill(lhs.getNext(), gen, kill, conditional); } else { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { computeGenKill(c, gen, kill, conditional); } } return; } } private void addToSetIfLocal(Node node, BitSet set) { Preconditions.checkState(NodeUtil.isName(node)); String name = node.getString(); if (!jsScope.isDeclared(name, false)) { return; } Var var = jsScope.getVar(name); if (!escaped.contains(var)) { set.set(var.index); } } /** * Give up computing liveness of formal parameter by putting all the parameter * names in the escaped set. */ void markAllParametersEscaped() { Node lp = jsScope.getRootNode().getFirstChild().getNext(); for(Node arg = lp.getFirstChild(); arg != null; arg = arg.getNext()) { escaped.add(jsScope.getVar(arg.getString())); } } private boolean isArgumentsName(Node n) { if (n.getType() != Token.NAME || !n.getString().equals(ARGUMENT_ARRAY_ALIAS) || jsScope.isDeclared(ARGUMENT_ARRAY_ALIAS, false)) { return false; } else { return true; } } }

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> != null) { check(externsRoot, true); } check(jsRoot, false); potentialChecks.flush(); } /** Main entry point of this phase for testing code. */ public Scope processForTesting(Node externsRoot, Node jsRoot) { Preconditions.checkState(scopeCreator == null); Preconditions.checkState(topScope == null); Preconditions.checkState(jsRoot.getParent() != null); Node externsAndJsRoot = jsRoot.getParent(); scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler)); topScope = scopeCreator.createScope(externsAndJsRoot, null); TypeInferencePass inference = new TypeInferencePass(compiler, reverseInterpreter, topScope, scopeCreator); inference.process(externsRoot, jsRoot); process(externsRoot, jsRoot); return topScope; } public void check(Node node, boolean externs) { Preconditions.checkNotNull(node); NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator); inExterns = externs; t.traverseWithScope(node, topScope); if (externs) { inferJSDocInfo.process(node, null); } else { inferJSDocInfo.process(null, node); } } public boolean shouldTraverse( NodeTraversal t, Node n, Node parent) { JSDocInfo info; switch (n.getType()) { case Token.SCRIPT: case Token.VAR: // @notypecheck info = n.getJSDocInfo(); if (info != null && info.isNoTypeCheck()) { return false; } break; case Token.FUNCTION: // @notypecheck info = n.getJSDocInfo(); info = (info == null) ? parent.getJSDocInfo() : info; if (info != null && info.isNoTypeCheck()) { return false; } // normal type checking final TypeCheck outerThis = this; final Scope outerScope = t.getScope(); final FunctionType functionType = (FunctionType) n.getJSType(); final String functionPrivateName = n.getFirstChild().getString(); if (functionPrivateName != null

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> && functionPrivateName.length() > 0 && outerScope.isDeclared(functionPrivateName, false) && // Ideally, we would want to check whether the type in the scope // differs from the type being defined, but then the extern // redeclarations of built-in types generates spurious warnings. !(outerScope.getVar( functionPrivateName).getType() instanceof FunctionType)) { t.report(n, FUNCTION_MASKS_VARIABLE, functionPrivateName); } // TODO(user): Only traverse the function's body. The function's // name and arguments are traversed by the scope creator, and ideally // should not be traversed by the type checker. break; } return true; } /** * This is the meat of the type checking. It is basically one big switch, * with each case representing one type of parse tree node. The individual * cases are usually pretty straightforward. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of the node n. */ public void visit(NodeTraversal t, Node n, Node parent) { JSType childType; JSType leftType, rightType; Node left, right; // To be explicitly set to false if the node is not typeable. boolean typeable = true; switch (n.getType()) { case Token.NAME: typeable = visitName(t, n, parent); break; case Token.LP: // If this is under a FUNCTION node, it is a parameter list and can be // ignored here. if (parent.getType() != Token.FUNCTION) { ensureTyped(t, n, getJSType(n.getFirstChild())); } else { typeable = false; } break; case Token.COMMA: ensureTyped(t, n, getJSType(n.getLastChild())); break; case Token.TRUE: case Token.FALSE: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.THIS: ensureTyped(t, n, t.getScope().getTypeOfThis());

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> break; case Token.REF_SPECIAL: ensureTyped(t, n); break; case Token.GET_REF: ensureTyped(t, n, getJSType(n.getFirstChild())); break; case Token.NULL: ensureTyped(t, n, NULL_TYPE); break; case Token.NUMBER: if (n.getParent().getType() != Token.OBJECTLIT) { ensureTyped(t, n, NUMBER_TYPE); } else { typeable = false; } break; case Token.ARRAYLIT: ensureTyped(t, n, ARRAY_TYPE); break; case Token.STRING: if (n.getParent().getType() != Token.OBJECTLIT) { ensureTyped(t, n, STRING_TYPE); } else { typeable = false; } break; case Token.REGEXP: ensureTyped(t, n, REGEXP_TYPE); break; case Token.GETPROP: visitGetProp(t, n, parent); typeable = !(parent.getType() == Token.ASSIGN && parent.getFirstChild() == n); break; case Token.GETELEM: visitGetElem(t, n); // The type of GETELEM is always unknown, so no point counting that. // If that unknown leaks elsewhere (say by an assignment to another // variable), then it will be counted. typeable = false; break; case Token.VAR: visitVar(t, n); typeable = false; break; case Token.NEW: visitNew(t, n); typeable = true; break; case Token.CALL: visitCall(t, n); typeable = !NodeUtil.isExpressionNode(parent); break; case Token.RETURN: visitReturn(t, n); typeable = false; break; case Token.DEC: case Token.INC: left = n.getFirstChild(); validator.expectNumber( t, left, getJSType(left), "increment/decrement"); ensureTyped(t, n, NUMBER_TYPE); break; case Token.NOT: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.VOID:

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> ensureTyped(t, n, VOID_TYPE); break; case Token.TYPEOF: ensureTyped(t, n, STRING_TYPE); break; case Token.BITNOT: childType = getJSType(n.getFirstChild()); if (!childType.matchesInt32Context()) { t.report(n, BIT_OPERATION, NodeUtil.opToStr(n.getType()), childType.toString()); } ensureTyped(t, n, NUMBER_TYPE); break; case Token.POS: case Token.NEG: left = n.getFirstChild(); validator.expectNumber(t, left, getJSType(left), "sign operator"); ensureTyped(t, n, NUMBER_TYPE); break; case Token.EQ: case Token.NE: { leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined(); JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined(); TernaryValue result = leftTypeRestricted.testForEquality(rightTypeRestricted); if (result != TernaryValue.UNKNOWN) { if (n.getType() == Token.NE) { result = result.not(); } t.report(n, DETERMINISTIC_TEST, leftType.toString(), rightType.toString(), result.toString()); } ensureTyped(t, n, BOOLEAN_TYPE); break; } case Token.SHEQ: case Token.SHNE: { leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined(); JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined(); if (!leftTypeRestricted.canTestForShallowEqualityWith( rightTypeRestricted)) { t.report(n, DETERMINISTIC_TEST_NO_RESULT, leftType.toString(), rightType.toString()); } ensureTyped(t, n, BOOLEAN_TYPE); break; } case Token.LT: case Token.LE: case Token.GT: case Token.GE: left

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>Type = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); if (rightType.isNumber()) { validator.expectNumber( t, n, leftType, "left side of numeric comparison"); } else if (leftType.isNumber()) { validator.expectNumber( t, n, rightType, "right side of numeric comparison"); } else if (leftType.matchesNumberContext() && rightType.matchesNumberContext()) { // OK. } else { // Whether the comparison is numeric will be determined at runtime // each time the expression is evaluated. Regardless, both operands // should match a string context. String message = "left side of comparison"; validator.expectString(t, n, leftType, message); validator.expectNotVoid( t, n, leftType, message, getNativeType(STRING_TYPE)); message = "right side of comparison"; validator.expectString(t, n, rightType, message); validator.expectNotVoid( t, n, rightType, message, getNativeType(STRING_TYPE)); } ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.IN: left = n.getFirstChild(); right = n.getLastChild(); leftType = getJSType(left); rightType = getJSType(right); validator.expectObject(t, n, rightType, "'in' requires an object"); validator.expectString(t, left, leftType, "left side of 'in'"); ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.INSTANCEOF: left = n.getFirstChild(); right = n.getLastChild(); leftType = getJSType(left); rightType = getJSType(right).restrictByNotNullOrUndefined(); validator.expectAnyObject( t, left, leftType, "deterministic instanceof yields false"); validator.expectActualObject( t, right, rightType, "instanceof requires an object"); ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.ASSIGN: visitAssign(t, n); typeable = false; break; case Token.ASSIGN_LSH: case Token.ASSIGN

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_SUB: case Token.ASSIGN_ADD: case Token.ASSIGN_MUL: case Token.LSH: case Token.RSH: case Token.URSH: case Token.DIV: case Token.MOD: case Token.BITOR: case Token.BITXOR: case Token.BITAND: case Token.SUB: case Token.ADD: case Token.MUL: visitBinaryOperator(n.getType(), t, n); break; case Token.DELPROP: if (!isReference(n.getFirstChild())) { t.report(n, BAD_DELETE); } ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.CASE: JSType switchType = getJSType(parent.getFirstChild()); JSType caseType = getJSType(n.getFirstChild()); validator.expectSwitchMatchesCase(t, n, switchType, caseType); typeable = false; break; case Token.WITH: { Node child = n.getFirstChild(); childType = getJSType(child); validator.expectObject( t, child, childType, "with requires an object"); typeable = false; break; } case Token.FUNCTION: visitFunction(t, n); break; // These nodes have no interesting type behavior. case Token.LABEL: case Token.LABEL_NAME: case Token.SWITCH: case Token.BREAK: case Token.CATCH: case Token.TRY: case Token.SCRIPT: case Token.EXPR_RESULT: case Token.BLOCK: case Token.EMPTY: case Token.DEFAULT: case Token.CONTINUE: case Token.DEBUGGER: case Token.THROW: typeable = false; break; // These nodes require data flow analysis. case Token.DO: case Token.FOR: case Token.IF: case Token.WHILE: typeable = false; break; // These nodes are typed

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> (!namedType.isResolved()) { return namedType.getReferenceName(); } } else if (type.isUnionType()) { for (JSType alt : ((UnionType) type).getAlternates()) { if (alt.isUnknownType()) { String unresolvedReference = getUnresolvedReference(alt); if (unresolvedReference != null) { return unresolvedReference; } } } } return null; } /** * Visits an assignment <code>lvalue = rvalue</code>. If the * <code>lvalue</code> is a prototype modification, we change the schema * of the object type it is referring to. * @param t the traversal * @param assign the assign node * (<code>assign.getType() == Token.ASSIGN</code> is an implicit invariant) */ private void visitAssign(NodeTraversal t, Node assign) { JSDocInfo info = assign.getJSDocInfo(); Node lvalue = assign.getFirstChild(); Node rvalue = assign.getLastChild(); if (lvalue.getType() == Token.GETPROP) { Node object = lvalue.getFirstChild(); JSType objectJsType = getJSType(object); String property = lvalue.getLastChild().getString(); // the first name in this getprop refers to an interface // we perform checks in addition to the ones below if (object.getType() == Token.GETPROP) { JSType jsType = getJSType(object.getFirstChild()); if (jsType.isInterface() && object.getLastChild().getString().equals("prototype")) { visitInterfaceGetprop(t, assign, object, property, lvalue, rvalue); } } // /** @type ... */object.name = ...; if (info != null && info.hasType()) { visitAnnotatedAssignGetprop(t, assign, info.getType().evaluate(t.getScope()), object, property, rvalue); return; } // /** @enum ... */object.name = ...; if (info != null && info.hasEnumParameterType()) { checkEnumInitializer( t, rvalue, info.getEnumParameterType().evaluate(t.getScope())); return; } //

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> object.prototype = ...; if (property.equals("prototype")) { if (objectJsType instanceof FunctionType) { FunctionType functionType = (FunctionType) objectJsType; if (functionType.isConstructor()) { JSType rvalueType = rvalue.getJSType(); validator.expectObject(t, rvalue, rvalueType, OVERRIDING_PROTOTYPE_WITH_NON_OBJECT); } } else { // TODO(user): might want to flag that } return; } // object.prototype.property = ...; if (object.getType() == Token.GETPROP) { Node object2 = object.getFirstChild(); String property2 = NodeUtil.getStringValue(object.getLastChild()); if ("prototype".equals(property2)) { JSType jsType = object2.getJSType(); if (jsType instanceof FunctionType) { FunctionType functionType = (FunctionType) jsType; if (functionType.isConstructor() || functionType.isInterface()) { checkDeclaredPropertyInheritance( t, assign, functionType, property, info, getJSType(rvalue)); } } else { // TODO(user): might want to flag that } return; } } // object.property = ...; ObjectType type = ObjectType.cast( objectJsType.restrictByNotNullOrUndefined()); if (type != null) { if (type.hasProperty(property) && !type.isPropertyTypeInferred(property) && !propertyIsImplicitCast(type, property)) { validator.expectCanAssignToPropertyOf( t, assign, getJSType(rvalue), type.getPropertyType(property), object, property); } return; } } else if (lvalue.getType() == Token.NAME) { // variable with inferred type case JSType rvalueType = getJSType(assign.getLastChild()); Var var = t.getScope().getVar(lvalue.getString()); if (var != null) { if (var.isTypeInferred()) { return; } } } // fall through case JSType leftType = getJSType(lvalue); Node rightChild = assign.getLastChild(); JSType rightType = getJS

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> LP nodes) and // variable declarations are ignored. // TODO(user): remove this short-circuiting in favor of a // pre order traversal of the FUNCTION, CATCH, LP and VAR nodes. int parentNodeType = parent.getType(); if (parentNodeType == Token.FUNCTION || parentNodeType == Token.CATCH || parentNodeType == Token.LP || parentNodeType == Token.VAR) { return false; } JSType type = n.getJSType(); if (type == null) { type = getNativeType(UNKNOWN_TYPE); Var var = t.getScope().getVar(n.getString()); if (var != null) { JSType varType = var.getType(); if (varType != null) { type = varType; } } } ensureTyped(t, n, type); return true; } /** * Visits a GETPROP node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of <code>n</code> */ private void visitGetProp(NodeTraversal t, Node n, Node parent) { // GETPROP nodes have an assigned type on their node by the scope creator // if this is an enum declaration. The only namespaced enum declarations // that we allow are of the form object.name = ...; if (n.getJSType() != null && parent.getType() == Token.ASSIGN) { return; } // obj.prop or obj.method() // Lots of types can appear on the left, a call to a void function can // never be on the left. getPropertyType will decide what is acceptable // and what isn't. Node property = n.getLastChild(); Node objNode = n.getFirstChild(); JSType childType = getJSType(objNode); // TODO(user): remove in favor of flagging every property access on // non-object. if (!validator.expectNotVoid(t, n, childType, "undefined has no properties", getNativeType(OBJECT_TYPE))) { ensureTyped(t, n); return; } check

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>PropertyAccess(childType, property.getString(), t, n); ensureTyped(t, n); } /** * Make sure that the access of this property is ok. */ private void checkPropertyAccess(JSType childType, String propName, NodeTraversal t, Node n) { ObjectType objectType = childType.dereference(); if (objectType != null) { JSType propType = getJSType(n); if ((!objectType.hasProperty(propName) || objectType.equals(typeRegistry.getNativeType(UNKNOWN_TYPE))) && propType.equals(typeRegistry.getNativeType(UNKNOWN_TYPE))) { if (objectType instanceof EnumType) { t.report(n, INEXISTENT_ENUM_ELEMENT, propName); } else if (!objectType.isEmptyType() && reportMissingProperties && !isPropertyTest(n)) { if (!typeRegistry.canPropertyBeDefined(objectType, propName)) { t.report(n, INEXISTENT_PROPERTY, propName, validator.getReadableJSTypeName(n.getFirstChild(), true)); } } } } else { // TODO(nicksantos): might want to flag the access on a non object when // it's impossible to get a property from this type. } } /** * Determines whether this node is testing for the existence of a property. * If true, we will not emit warnings about a missing property. * * @param getProp The GETPROP being tested. */ private boolean isPropertyTest(Node getProp) { Node parent = getProp.getParent(); switch (parent.getType()) { case Token.CALL: return parent.getFirstChild() != getProp && compiler.getCodingConvention().isPropertyTestFunction(parent); case Token.IF: case Token.WHILE: case Token.DO: case Token.FOR: return NodeUtil.getConditionExpression(parent) == getProp; case Token.INSTANCEOF: case Token.TYPEOF: return true; case Token.AND: case Token.HOOK: return parent.getFirstChild() == getProp; } return false; } /** * Visits a GETELEM node. * *

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitGetElem(NodeTraversal t, Node n) { Node left = n.getFirstChild(); Node right = n.getLastChild(); validator.expectIndexMatch(t, n, getJSType(left), getJSType(right)); ensureTyped(t, n); } /** * Visits a VAR node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitVar(NodeTraversal t, Node n) { // TODO(nicksantos): Fix this so that the doc info always shows up // on the NAME node. We probably want to wait for the parser // merge to fix this. JSDocInfo varInfo = n.hasOneChild() ? n.getJSDocInfo() : null; for (Node name : n.children()) { Node value = name.getFirstChild(); // A null var would indicate a bug in the scope creation logic. Var var = t.getScope().getVar(name.getString()); if (value != null) { JSType valueType = getJSType(value); JSType nameType = var.getType(); nameType = (nameType == null) ? getNativeType(UNKNOWN_TYPE) : nameType; JSDocInfo info = name.getJSDocInfo(); if (info == null) { info = varInfo; } if (info != null && info.hasEnumParameterType()) { // var.getType() can never be null, this would indicate a bug in the // scope creation logic. checkEnumInitializer( t, value, info.getEnumParameterType().evaluate(t.getScope())); } else if (var.isTypeInferred()) { ensureTyped(t, name, valueType); } else { validator.expectCanAssignTo( t, value, valueType, nameType, "initializing variable"); } } } } /** * Visits a

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> NEW node. */ private void visitNew(NodeTraversal t, Node n) { Node constructor = n.getFirstChild(); FunctionType type = getFunctionType(constructor); if (type != null && type.isConstructor()) { visitParameterList(t, n, type); ensureTyped(t, n, type.getInstanceType()); } else { // TODO(user): add support for namespaced objects. if (constructor.getType() != Token.GETPROP) { // TODO(user): make the constructor node have lineno/charno // and use constructor for a more precise error indication. // It seems that GETPROP nodes are missing this information. Node line; if (constructor.getLineno() < 0 || constructor.getCharno() < 0) { line = n; } else { line = constructor; } t.report(line, NOT_A_CONSTRUCTOR); } ensureTyped(t, n); } } /** * Visits a {@link Token#FUNCTION} node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitFunction(NodeTraversal t, Node n) { JSDocInfo info = n.getJSDocInfo(); FunctionType functionType = (FunctionType) n.getJSType(); String functionPrivateName = n.getFirstChild().getString(); if (functionType.isInterface() || functionType.isConstructor()) { FunctionType baseConstructor = functionType. getPrototype().getImplicitPrototype().getConstructor(); if (baseConstructor != null && baseConstructor != getNativeType(OBJECT_FUNCTION_TYPE) && (baseConstructor.isConstructor() && functionType.isInterface() || baseConstructor.isInterface() && functionType.isConstructor())) { compiler.report( t.makeError(n, CONFLICTING_EXTENDED_TYPE, functionPrivateName)); } for (JSType baseInterface : functionType.getImplementedInterfaces()) { boolean badImplementedType = false; ObjectType baseInterfaceObj = ObjectType.cast(baseInterface); if (baseInterfaceObj != null) { FunctionType interfaceConstructor

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> = baseInterfaceObj.getConstructor(); if (interfaceConstructor != null && !interfaceConstructor.isInterface()) { badImplementedType = true; } } else { badImplementedType = true; } if (badImplementedType) { t.report(n, BAD_IMPLEMENTED_TYPE, functionPrivateName); } } if (functionType.isConstructor()) { validator.expectAllInterfacePropertiesImplemented(functionType); } } } /** * Visits a CALL node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitCall(NodeTraversal t, Node n) { Node child = n.getFirstChild(); JSType childType = getJSType(child).restrictByNotNullOrUndefined(); if (!childType.canBeCalled()) { t.report(n, NOT_CALLABLE, childType.toString()); ensureTyped(t, n); return; } // A couple of types can be called as if they were functions. // If it is a function type, then validate parameters. if (childType instanceof FunctionType) { FunctionType functionType = (FunctionType) childType; // Non-native constructors should never be called directly. if (functionType.isConstructor() && !functionType.isNativeObjectType()) { t.report(n, CONSTRUCTOR_NOT_CALLABLE, childType.toString()); } visitParameterList(t, n, functionType); ensureTyped(t, n, functionType.getReturnType()); } else { ensureTyped(t, n); } // TODO: Add something to check for calls of RegExp objects, which is not // supported by IE. Either say something about the return type or warn // about the non-portability of the call or both. } /** * Visits the parameters of a CALL or a NEW node. */ private void visitParameterList(NodeTraversal t, Node call, FunctionType functionType) { Iterator<Node> arguments = call.children().iterator(); arguments.next(); // skip the function name Iterator<Node> parameters = functionType.

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>getParameters().iterator(); int ordinal = 0; while (arguments.hasNext() && parameters.hasNext()) { Node parameter = parameters.next(); Node argument = arguments.next(); ordinal++; validator.expectArgumentMatchesParameter(t, argument, getJSType(argument), getJSType(parameter), call, ordinal); } int numArgs = call.getChildCount() - 1; int minArgs = functionType.getMinArguments(); int maxArgs = functionType.getMaxArguments(); if (minArgs > numArgs || maxArgs < numArgs) { t.getCompiler().report( t.makeError(call, WRONG_ARGUMENT_COUNT, validator.getReadableJSTypeName(call.getFirstChild(), false), String.valueOf(numArgs), String.valueOf(minArgs), maxArgs != Integer.MAX_VALUE ? " and no more than " + maxArgs + " argument(s)" : "")); } } /** * Visits a RETURN node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitReturn(NodeTraversal t, Node n) { Node function = t.getEnclosingFunction(); // This is a misplaced return, but the real JS will fail to compile, // so let it go. if (function == null) { return; } JSType jsType = getJSType(function); if (jsType instanceof FunctionType) { FunctionType functionType = (FunctionType) jsType; JSType returnType = functionType.getReturnType(); // if no return type is specified, undefined must be returned // (it's a void function) if (returnType == null) { returnType = getNativeType(VOID_TYPE); } // fetching the returned value's type Node valueNode = n.getFirstChild(); JSType actualReturnType; if (valueNode == null) { actualReturnType = getNativeType(VOID_TYPE); valueNode = n; } else { actualReturnType = getJSType(valueNode); } // verifying validator.expectCanAssignTo(t, valueNode, actualReturnType, returnType,

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> "inconsistent return type"); } } /** * This function unifies the type checking involved in the core binary * operators and the corresponding assignment operators. The representation * used internally is such that common code can handle both kinds of * operators easily. * * @param op The operator. * @param t The traversal object, needed to report errors. * @param n The node being checked. */ private void visitBinaryOperator(int op, NodeTraversal t, Node n) { Node left = n.getFirstChild(); JSType leftType = getJSType(left); Node right = n.getLastChild(); JSType rightType = getJSType(right); switch (op) { case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.LSH: case Token.RSH: case Token.ASSIGN_URSH: case Token.URSH: if (!leftType.matchesInt32Context()) { t.report(left, BIT_OPERATION, NodeUtil.opToStr(n.getType()), leftType.toString()); } if (!rightType.matchesUint32Context()) { t.report(right, BIT_OPERATION, NodeUtil.opToStr(n.getType()), rightType.toString()); } break; case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN_MUL: case Token.ASSIGN_SUB: case Token.DIV: case Token.MOD: case Token.MUL: case Token.SUB: validator.expectNumber(t, left, leftType, "left operand"); validator.expectNumber(t, right, rightType, "right operand"); break; case Token.ASSIGN_BITAND: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITOR: case Token.BITAND: case Token.BITXOR: case Token.BITOR: validator.expectBitwiseable(t, left, leftType, "bad left operand to bitwise operator"); validator.expectBitwiseable(t, right, rightType, "bad right operand to bitwise operator"); break; case Token.ASSIGN_ADD: case Token.ADD: break;

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> default: t.report(n, UNEXPECTED_TOKEN, Node.tokenToName(op)); } ensureTyped(t, n); } /** * <p>Checks the initializer of an enum. An enum can be initialized with an * object literal whose values must be subtypes of the declared enum element * type, or by copying another enum.</p> * * <p>In the case of an enum copy, we verify that the enum element type of the * enum used for initialization is a subtype of the enum element type of * the enum the value is being copied in.</p> * * <p>Examples:</p> * <pre>var myEnum = {FOO: ..., BAR: ...}; * var myEnum = myOtherEnum;</pre> * * @param value the value used for initialization of the enum * @param primitiveType The type of each element of the enum. */ private void checkEnumInitializer( NodeTraversal t, Node value, JSType primitiveType) { if (value.getType() == Token.OBJECTLIT) { // re-using value as the value of the object literal and advancing twice value = value.getFirstChild(); value = (value == null) ? null : value.getNext(); while (value != null) { // the value's type must be assignable to the enum's primitive type validator.expectCanAssignTo(t, value, getJSType(value), primitiveType, "element type must match enum's type"); // advancing twice value = value.getNext(); value = (value == null) ? null : value.getNext(); } } else if (value.getJSType() instanceof EnumType) { // TODO(user): Remove the instanceof check in favor // of a type.isEnumType() predicate. Currently, not all enum types are // implemented by the EnumClass, e.g. the unknown type and the any // type. The types need to be defined by interfaces such that an // implementation can implement multiple types interface. EnumType valueEnumType = (EnumType) value.getJSType(); JSType valueEnumPrimitiveType = valueEnumType.getElementsType().getPrimitiveType(); validator.expectCanAssignTo(t, value, valueEnumPrimitiveType, primitive

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> Preconditions.checkState(expectedScopes.size() == actualScopes.size()); for (int i = 0; i < expectedScopes.size(); i++) { Scope expectedScope = expectedScopes.get(i); Scope actualScope = actualScopes.get(i); if (!checkNodesMatch(expectedScope.getRootNode(), actualScope.getRootNode())) { compiler.report( JSError.make( SCOPE_MISMATCH, expectedScope.getRootNode().toStringTree(), actualScope.getRootNode().toStringTree())); continue; } if (expectedScope.getVarCount() != actualScope.getVarCount()) { compiler.report( JSError.make( VARIABLE_COUNT_MISMATCH, Integer.toString(expectedScope.getVarCount()), Integer.toString(actualScope.getVarCount()))); } else { Iterator<Var> it = expectedScope.getVars(); while (it.hasNext()) { Var var = it.next(); Scope.Var actualVar = actualScope.getVar(var.getName()); if (actualVar == null || expectedScope.getVar(var.getName()) != var) { compiler.report( JSError.make(MISSING_VARIABLE, var.getName())); } else if ( !checkNodesMatch( var.getNameNode(), actualVar.getNameNode()) || !isNodeAttached(actualVar.getNameNode())) { compiler.report( JSError.make(MOVED_VARIABLE, var.getName())); } } } } } /** * Check that the two nodes have the same relative position in the tree. */ private boolean checkNodesMatch(Node nodeA, Node nodeB) { Node currentA = nodeA; Node currentB = nodeB; while (currentA != null && currentB != null) { if (currentA.getType() != currentB.getType() || !currentA.isEquivalentTo(currentB)) { return false; } currentA = currentA.getParent(); currentB = currentB.getParent(); } return currentA == null && currentB == null; } private boolean isNodeAttached(Node node) { // Make sure the cached var is still attached. for (Node current = node; current != null; current

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> // to that node. if (fileOverviewInfo != null) { if ((irNode.getJSDocInfo() != null) && (irNode.getJSDocInfo().getLicense() != null)) { fileOverviewInfo.setLicense(irNode.getJSDocInfo().getLicense()); } irNode.setJSDocInfo(fileOverviewInfo); } } return irNode; } private Node transformBlock(AstNode node) { Node irNode = transform(node); if (irNode.getType() != Token.BLOCK) { if (irNode.getType() == Token.EMPTY) { irNode.setType(Token.BLOCK); irNode.setWasEmptyNode(true); } else { Node newBlock = new Node(Token.BLOCK, irNode, irNode.getLineno(), irNode.getCharno()); irNode = newBlock; } } return irNode; } private Node transform(AstNode node) { String jsDoc = node.getJsDoc(); NodeWithJsDoc nodeWithJsDoc = null; if (jsDoc != null) { nodeWithJsDoc = new NodeWithJsDoc(); nodesWithJsDoc.put(jsDoc, nodeWithJsDoc); } Node irNode = justTransform(node); if (nodeWithJsDoc != null) { nodeWithJsDoc.node = irNode; } // If we have a named function, set the position to that of the name. if (irNode.getType() == Token.FUNCTION && irNode.getFirstChild().getLineno() != -1) { irNode.setLineno(irNode.getFirstChild().getLineno()); irNode.setCharno(irNode.getFirstChild().getCharno()); } else { if (irNode.getLineno() == -1) { // If we didn't already set the line, then set it now. This avoids // cases like ParenthesizedExpression where we just return a previous // node, but don't want the new node to get its parent's line number. int lineno = node.getLineno(); irNode.setLineno(lineno); int charno = position2charno(node.get

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> if (node.getType() == Token.NAME) { registry.identifyEnumName(node.getString()); } else if (node.getType() == Token.VAR && node.getChildCount() == 1) { registry.identifyEnumName( node.getFirstChild().getString()); } else if (node.getType() == Token.ASSIGN) { registry.identifyEnumName( node.getFirstChild().getQualifiedName()); } } } private int position2charno(int position) { int lineIndex = sourceString.lastIndexOf('\n', position); if (lineIndex == -1) { return position; } else { // Subtract one for initial position being 0. return position - lineIndex - 1; } } private Node justTransform(AstNode node) { return transformDispatcher.process(node); } private class TransformDispatcher extends TypeSafeDispatcher<Node> { private Node processGeneric( com.google.javascript.jscomp.mozilla.rhino.Node n) { Node node = new Node(transformTokenType(n.getType())); for (com.google.javascript.jscomp.mozilla.rhino.Node child : n) { node.addChildToBack(transform((AstNode)child)); } return node; } /** * Transforms the given node and then sets its type to Token.STRING if it * was Token.NAME. If its type was already Token.STRING, then quotes it. * Used for properties, as the old AST uses String tokens, while the new one * uses Name tokens for unquoted strings. For example, in * var o = {'a' : 1, b: 2}; * the string 'a' is quoted, while the name b is turned into a string, but * unquoted. */ private Node transformAsString(AstNode n) { Node ret = transform(n); if (ret.getType() == Token.STRING) { ret.putBooleanProp(Node.QUOTED_PROP, true); } else if (ret.getType() == Token.NAME) { ret.setType(Token.STRING); } return ret; } @Override Node processArrayLiteral(ArrayLiteral literalNode) { if (literalNode.isDestruct

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>uring()) { reportDestructuringAssign(literalNode); } Node node = new Node(Token.ARRAYLIT); int skipCount = 0; for (AstNode child : literalNode.getElements()) { Node c = transform(child); if (c.getType() == Token.EMPTY) { skipCount++; } node.addChildToBack(c); } if (skipCount > 0) { int[] skipIndexes = new int[skipCount]; int i = 0; int j = 0; for (Node child : node.children()) { if (child.getType() == Token.EMPTY) { node.removeChild(child); skipIndexes[j] = i; j++; } i++; } node.putProp(Node.SKIP_INDEXES_PROP, skipIndexes); } return node; } @Override Node processAssignment(Assignment assignmentNode) { return processInfixExpression(assignmentNode); } @Override Node processAstRoot(AstRoot rootNode) { Node node = new ScriptOrFnNode(Token.SCRIPT); for (com.google.javascript.jscomp.mozilla.rhino.Node child : rootNode) { node.addChildToBack(transform((AstNode)child)); } parseDirectives(node); return node; } /** * Parse the directives, encode them in the AST, and remove their nodes. * * For information on ES5 directives, see section 14.1 of * Ecma-262, Edition 5. * * It would be nice if Rhino would eventually take care of this for * us, but right now their directive-processing is a one-off. */ private void parseDirectives(Node node) { // Remove all the directives, and encode them in the AST. Set<String> directives = null; while (isDirective(node.getFirstChild())) { String directive = node.removeFirstChild().getFirstChild().getString(); if (directives == null) { directives = Sets.newHashSet(directive); } else { directives.add(directive); } } if (directives != null) { node.setDirectives(directives); } } private

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> boolean isDirective(Node n) { if (n == null) return false; int nType = n.getType(); return (nType == Token.EXPR_RESULT || nType == Token.EXPR_VOID) && n.getFirstChild().getType() == Token.STRING && ALLOWED_DIRECTIVES.contains(n.getFirstChild().getString()); } @Override Node processBlock(Block blockNode) { return processGeneric(blockNode); } @Override Node processBreakStatement(BreakStatement statementNode) { Node node = new Node(Token.BREAK); if (statementNode.getBreakLabel() != null) { Node labelName = transform(statementNode.getBreakLabel()); // Change the NAME to LABEL_NAME labelName.setType(Token.LABEL_NAME); node.addChildToBack(labelName); } return node; } @Override Node processCatchClause(CatchClause clauseNode) { AstNode catchVar = clauseNode.getVarName(); Node node = new Node(Token.CATCH, transform(catchVar)); if (clauseNode.getCatchCondition() != null) { node.addChildToBack(transform(clauseNode.getCatchCondition())); } else { Node catchCondition = new Node(Token.EMPTY); // Old Rhino used the position of the catchVar as the position // for the (nonexistent) error being caught. catchCondition.setLineno(catchVar.getLineno()); int clauseAbsolutePosition = position2charno(catchVar.getAbsolutePosition()); catchCondition.setCharno(clauseAbsolutePosition); node.addChildToBack(catchCondition); } node.addChildToBack(transformBlock(clauseNode.getBody())); return node; } @Override Node processConditionalExpression(ConditionalExpression exprNode) { return new Node( Token.HOOK, transform(exprNode.getTestExpression()), transform(exprNode.getTrueExpression()), transform(exprNode.getFalseExpression())); } @Override Node processContinueStatement(ContinueStatement statementNode) { Node node = new Node(Token.CONTINUE); if (statementNode.getLabel() != null) { Node labelName = transform(statementNode.getLabel()); // Change the NAME to LABEL_NAME

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> } Node parseTestCode(String js) { initCompilerOptionsIfTesting(); CompilerInput input = new CompilerInput( JSSourceFile.fromCode(" [testcode] ", js)); if (inputsByName == null) { inputsByName = Maps.newHashMap(); } inputsByName.put(input.getName(), input); return input.getAstRoot(this); } @Override ErrorReporter getDefaultErrorReporter() { return defaultErrorReporter; } //------------------------------------------------------------------------ // Convert back to source code //------------------------------------------------------------------------ /** * Converts the main parse tree back to js code. */ public String toSource() { return runInCompilerThread(new Callable<String>() { public String call() throws Exception { Tracer tracer = newTracer("toSource"); try { CodeBuilder cb = new CodeBuilder(); if (jsRoot != null) { int i = 0; for (Node scriptNode = jsRoot.getFirstChild(); scriptNode != null; scriptNode = scriptNode.getNext()) { toSource(cb, i++, scriptNode); } } return cb.toString(); } finally { stopTracer(tracer, "toSource"); } } }); } /** * Converts the parse tree for each input back to js code. */ public String[] toSourceArray() { return runInCompilerThread(new Callable<String[]>() { public String[] call() throws Exception { Tracer tracer = newTracer("toSourceArray"); try { int numInputs = inputs.size(); String[] sources = new String[numInputs]; CodeBuilder cb = new CodeBuilder(); for (int i = 0; i < numInputs; i++) { Node scriptNode = inputs.get(i).getAstRoot(Compiler.this); cb.reset(); toSource(cb, i, scriptNode); sources[i] = cb.toString(); } return sources; } finally { stopTracer(tracer, "toSourceArray"); } } }); } /** * Converts the parse tree for a module back to js code. */ public String toSource(final JSModule module) { return runInCompilerThread(new Callable<String>() { public String call

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> null; if (parent == null) { scope = new Scope(n, compiler); } else { scope = new Scope(parent, n); } scanRoot(n, parent); sourceName = null; Scope returnedScope = scope; scope = null; return returnedScope; } private void scanRoot(Node n, Scope parent) { if (n.getType() == Token.FUNCTION) { sourceName = (String) n.getProp(Node.SOURCENAME_PROP); final Node fnNameNode = n.getFirstChild(); final Node args = fnNameNode.getNext(); final Node body = args.getNext(); // Bleed the function name into the scope, if it hasn't // been declared in the outer scope. String fnName = fnNameNode.getString(); if (!fnName.isEmpty() && NodeUtil.isFunctionAnonymous(n)) { declareVar(fnName, fnNameNode, n, null, null, n); } // Args: Declare function variables Preconditions.checkState(args.getType() == Token.LP); for (Node a = args.getFirstChild(); a != null; a = a.getNext()) { Preconditions.checkState(a.getType() == Token.NAME); declareVar(a.getString(), a, args, n, null, n); } // Body scanVars(body, n); } else { // It's the global block Preconditions.checkState(scope.getParent() == null); scanVars(n, null); } } /** * Scans and gather variables declarations under a Node */ private void scanVars(Node n, Node parent) { switch (n.getType()) { case Token.VAR: // Declare all variables. e.g. var x = 1, y, z; for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); Preconditions.checkState(child.getType() == Token.NAME); String name = child.getString(); declareVar(name, child, n, parent, null, n); child = next; } return; case Token.FUNCTION: if (NodeUtil.isFunctionAnonymous(n)) { return; }

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> String fnName = n.getFirstChild().getString(); if (fnName.isEmpty()) { // This is invalid, but allow it so the checks can catch it. return; } declareVar(fnName, n.getFirstChild(), n, parent, null, n); return; // should not examine function's children case Token.CATCH: Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.getFirstChild().getType() == Token.NAME); // the first child is the catch var and the third child // is the code block final Node var = n.getFirstChild(); final Node block = var.getNext().getNext(); declareVar(var.getString(), var, n, parent, null, n); scanVars(block, n); return; // only one child to scan case Token.SCRIPT: sourceName = (String) n.getProp(Node.SOURCENAME_PROP); break; } // Variables can only occur in statement-level nodes, so // we only need to traverse children in a couple special cases. if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); scanVars(child, n); child = next; } } } /** * Interface for injectable duplicate handling. */ interface RedeclarationHandler { void onRedeclaration( Scope s, String name, Node n, Node parent, Node gramps, Node nodeWithLineNumber); } /** * The default handler for duplicate declarations. */ private class DefaultRedeclarationHandler implements RedeclarationHandler { public void onRedeclaration( Scope s, String name, Node n, Node parent, Node gramps, Node nodeWithLineNumber) { // Don't allow multiple variables to be declared at the top level scope if (scope.isGlobal()) { Scope.Var origVar = scope.getVar(name); Node origParent = origVar.getParentNode(); if (origParent.getType() == Token.CATCH && parent.getType() == Token.CATCH) { // Okay, both are 'catch(x)' variables. return

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>/* * Copyright 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.ObjectType; import java.nio.charset.Charset; /** * A code generator that outputs type annotations for functions and * constructors. * */ class TypedCodeGenerator extends CodeGenerator { TypedCodeGenerator(CodeConsumer consumer, Charset outputCharset) { super(consumer, outputCharset, true); } @Override void add(Node n, Context context) { Node parent = n.getParent(); if (parent.getType() == Token.BLOCK || parent.getType() == Token.SCRIPT) { if (n.getType() == Token.FUNCTION) { add(getFunctionAnnotation(n)); } else if (n.getType() == Token.EXPR_RESULT && n.getFirstChild().getType() == Token.ASSIGN) { Node rhs = n.getFirstChild().getFirstChild(); add(getTypeAnnotation(rhs)); } else if (n.getType() == Token.VAR && n.getFirstChild().getFirstChild() != null && n.getFirstChild().getFirstChild().getType() == Token.FUNCTION) { add(getFunctionAnnotation(n.getFirstChild().getFirstChild())); } } super.add(n, context); } private String getTypeAnnotation(Node node) { JSType type = node.getJSType();

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> if (type instanceof FunctionType) { return getFunctionAnnotation(node); } else if (type != null && !type.isUnknownType() && !type.isEmptyType() && !type.isVoidType()) { return "/** @type {" + node.getJSType() + "} */\n"; } else { return ""; } } /** * @param node A node for a function for which to generate a type annotation */ private String getFunctionAnnotation(Node node) { StringBuilder sb = new StringBuilder("/**\n"); if (node.getJSType().isUnknownType()) { return ""; } FunctionType funType = (FunctionType) node.getJSType(); // We need to use the child nodes of the function as the nodes for the // parameters of the function type do not have the real parameter names. // FUNCTION // NAME // LP // NAME param1 // NAME param2 Node fnNode = funType.getSource(); if (fnNode != null) { Node paramNode = NodeUtil.getFnParameters(fnNode).getFirstChild(); // Param types for (Node n : funType.getParameters()) { // Bail out if the paramNode is not there. if (paramNode == null) { break; } sb.append(" * @param {" + n.getJSType() + "} "); sb.append(paramNode.getString()); sb.append("\n"); paramNode = paramNode.getNext(); } } // Return type JSType retType = funType.getReturnType(); if (retType != null && !retType.isUnknownType() && !retType.isEmptyType()) { sb.append(" * @return {" + retType + "}\n"); } // Constructor/interface if (funType.isConstructor() || funType.isInterface()) { ObjectType superInstance = funType.getSuperClassConstructor().getInstanceType(); if (!superInstance.toString().equals("Object")) { sb.append(" * @extends {" + superInstance + "}\n"); } for (ObjectType interfaze : funType.getImplementedInterfaces()) { sb.append(" * @implements {" + interfaze + "}\n"); }

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> private JSType currentClass = null; CheckAccessControls(AbstractCompiler compiler) { this.compiler = compiler; this.validator = compiler.getTypeValidator(); } public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } public void enterScope(NodeTraversal t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); Node parent = n.getParent(); if (isDeprecatedFunction(n, parent)) { deprecatedDepth++; } if (methodDepth == 0) { currentClass = getClassOfMethod(n, parent); } methodDepth++; } } public void exitScope(NodeTraversal t) { if (!t.inGlobalScope()) { Node n = t.getScopeRoot(); Node parent = n.getParent(); if (isDeprecatedFunction(n, parent)) { deprecatedDepth--; } methodDepth--; if (methodDepth == 0) { currentClass = null; } } } /** * Gets the type of the class that "owns" a method, or null if * we know that its un-owned. */ private JSType getClassOfMethod(Node n, Node parent) { if (parent.getType() == Token.ASSIGN) { Node lValue = parent.getFirstChild(); if (lValue.isQualifiedName()) { if (lValue.getType() == Token.GETPROP) { // We have an assignment of the form "a.b = ...". JSType lValueType = lValue.getJSType(); if (lValueType != null && lValueType.isConstructor()) { // If a.b is a constructor, then everything in this function // belongs to the "a.b" type. return ((FunctionType) lValueType).getInstanceType(); } else { // If a.b is not a constructor, then treat this as a method // of whatever type is on "a". return normalizeClassType(lValue.getFirstChild().getJSType()); } } else { // We have an assignment of the form "a = ...", so pull the // type off the "a". return normalizeClassType(lValue.getJSType()); } }

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>precation(NodeTraversal t, Node n, Node parent) { // Don't bother checking definitions or constructors. if (parent.getType() == Token.FUNCTION || parent.getType() == Token.VAR || parent.getType() == Token.NEW) { return; } Scope.Var var = t.getScope().getVar(n.getString()); JSDocInfo docInfo = var == null ? null : var.getJSDocInfo(); if (docInfo != null && docInfo.isDeprecated() && shouldEmitDeprecationWarning(t, n, parent)) { if (docInfo.getDeprecationReason() != null) { compiler.report( t.makeError(n, DEPRECATED_NAME_REASON, n.getString(), docInfo.getDeprecationReason())); } else { compiler.report( t.makeError(n, DEPRECATED_NAME, n.getString())); } } } /** * Checks the given GETPROP node to ensure that access restrictions are * obeyed. */ private void checkPropertyDeprecation(NodeTraversal t, Node n, Node parent) { // Don't bother checking constructors. if (parent.getType() == Token.NEW) { return; } ObjectType objectType = ObjectType.cast(dereference(n.getFirstChild().getJSType())); String propertyName = n.getLastChild().getString(); if (objectType != null) { String deprecationInfo = getPropertyDeprecationInfo(objectType, propertyName); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( t.makeError(n, DEPRECATED_PROP_REASON, propertyName, validator.getReadableJSTypeName(n.getFirstChild(), true), deprecationInfo)); } else { compiler.report( t.makeError(n, DEPRECATED_PROP, propertyName, validator.getReadableJSTypeName(n.getFirstChild(), true))); } } } } /** * Determines whether the given name is visible in the current context. * @param t The current traversal. * @param name The name node. */ private void checkNameVisibility(NodeTraversal t, Node name, Node parent) {

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> Var var = t.getScope().getVar(name.getString()); if (var != null) { JSDocInfo docInfo = var.getJSDocInfo(); if (docInfo != null) { // If a name is private, make sure that we're in the same file. Visibility visibility = docInfo.getVisibility(); if (visibility == Visibility.PRIVATE && !t.getInput().getName().equals(docInfo.getSourceName())) { if (docInfo.isConstructor() && isValidPrivateConstructorAccess(parent)) { return; } compiler.report( t.makeError(name, BAD_PRIVATE_GLOBAL_ACCESS, name.getString(), docInfo.getSourceName())); } } } } /** * Determines whether the given property is visible in the current context. * @param t The current traversal. * @param getprop The getprop node. */ private void checkPropertyVisibility(NodeTraversal t, Node getprop, Node parent) { ObjectType objectType = ObjectType.cast(dereference(getprop.getFirstChild().getJSType())); String propertyName = getprop.getLastChild().getString(); if (objectType != null) { // Is this a normal property access, or are we trying to override // an existing property? boolean isOverride = t.inGlobalScope() && parent.getType() == Token.ASSIGN && parent.getFirstChild() == getprop; // Find the lowest property defined on a class with visibility // information. if (isOverride) { objectType = objectType.getImplicitPrototype(); } JSDocInfo docInfo = null; for (; objectType != null; objectType = objectType.getImplicitPrototype()) { docInfo = objectType.getOwnPropertyJSDocInfo(propertyName); if (docInfo != null && docInfo.getVisibility() != Visibility.INHERITED) { break; } } if (objectType == null) { // We couldn't find a visibility modifier; assume it's public. return; } boolean sameInput = t.getInput().getName().equals(docInfo.getSourceName()); Visibility visibility = docInfo.getVisibility(); JSType ownerType = normalizeClassType(objectType); if (isOverride)

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> { // Check an ASSIGN statement that's trying to override a property // on a superclass. JSDocInfo overridingInfo = parent.getJSDocInfo(); Visibility overridingVisibility = overridingInfo == null ? Visibility.INHERITED : overridingInfo.getVisibility(); // Check that (a) the property *can* be overridden, and // (b) that the visibility of the override is the same as the // visibility of the original property. if (visibility == Visibility.PRIVATE && !sameInput) { compiler.report( t.makeError(getprop, PRIVATE_OVERRIDE, objectType.toString())); } else if (overridingVisibility != Visibility.INHERITED && overridingVisibility != visibility) { compiler.report( t.makeError(getprop, VISIBILITY_MISMATCH, visibility.name(), objectType.toString(), overridingVisibility.name())); } } else { if (sameInput) { // private access is always allowed in the same file. return; } else if (visibility == Visibility.PRIVATE && (currentClass == null || ownerType.differsFrom(currentClass))) { if (docInfo.isConstructor() && isValidPrivateConstructorAccess(parent)) { return; } // private access is not allowed outside the file from a different // enclosing class. compiler.report( t.makeError(getprop, BAD_PRIVATE_PROPERTY_ACCESS, propertyName, validator.getReadableJSTypeName( getprop.getFirstChild(), true))); } else if (visibility == Visibility.PROTECTED) { // There are 3 types of legal accesses of a protected property: // 1) Accesses in the same file // 2) Overriding the property in a subclass // 3) Accessing the property from inside a subclass // The first two have already been checked for. if (currentClass == null || !currentClass.isSubtype(ownerType)) { compiler.report( t.makeError(getprop, BAD_PROTECTED_PROPERTY_ACCESS, propertyName, validator.getReadableJSTypeName( getprop.getFirstChild(), true))); } } } } } /** * Whether the given access of a private constructor is legal. * * For

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> example, * new PrivateCtor_(); // not legal * PrivateCtor_.newInstance(); // legal * x instanceof PrivateCtor_ // legal * * This is a weird special case, because our visibility system is inherited * from Java, and JavaScript has no distinction between classes and * constructors like Java does. * * We may want to revisit this if we decide to make the restrictions tighter. */ private static boolean isValidPrivateConstructorAccess(Node parent) { return parent.getType() != Token.NEW; } /** * Determines whether a deprecation warning should be emitted. * @param t The current traversal. * @param n The node which we are checking. * @param parent The parent of the node which we are checking. */ private boolean shouldEmitDeprecationWarning( NodeTraversal t, Node n, Node parent) { // In the global scope, there are only two kinds of accesses that should // be flagged for warnings: // 1) Calls of deprecated functions and methods. // 2) Instantiations of deprecated classes. // For now, we just let everything else by. if (t.inGlobalScope()) { if (!((parent.getType() == Token.CALL && parent.getFirstChild() == n) || n.getType() == Token.NEW)) { return false; } } // We can always assign to a deprecated property, to keep it up to date. if (n.getType() == Token.GETPROP && n == parent.getFirstChild() && NodeUtil.isAssignmentOp(parent)) { return false; } return !canAccessDeprecatedTypes(t); } /** * Returns whether it's currently ok to access deprecated names and * properties. * * There are 3 exceptions when we're allowed to use a deprecated * type or property: * 1) When we're in a deprecated function. * 2) When we're in a deprecated class. * 3) When we're in a static method of a deprecated class. */ private boolean canAccessDeprecatedTypes(NodeTraversal t) { Node scopeRoot = t.getScopeRoot(); Node scopeRootParent = scopeRoot.getParent(); return // Case #1 (deprecatedDepth > 0) || // Case #

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> this to generate deps files. (We're only using it for // symbol dependencies.) DependencyInfo info = (new JsFileParser(errorManager)).parseFile( getName(), getName(), getCode()); provides.addAll(info.getProvides()); requires.addAll(info.getRequires()); generatedDependencyInfoFromSource = true; } } } private static class DepsFinder { private final List<String> provides = Lists.newArrayList(); private final List<String> requires = Lists.newArrayList(); private final CodingConvention codingConvention = new ClosureCodingConvention(); void visitTree(Node n) { visitSubtree(n, null); } void visitSubtree(Node n, Node parent) { if (n.getType() == Token.CALL) { String require = codingConvention.extractClassNameIfRequire(n, parent); if (require != null) { requires.add(require); } String provide = codingConvention.extractClassNameIfProvide(n, parent); if (provide != null) { provides.add(provide); } return; } else if (parent != null && parent.getType() != Token.EXPR_RESULT && parent.getType() != Token.SCRIPT) { return; } for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { visitSubtree(child, n); } } } /** * Gets the source line for the indicated line number. * * @param lineNumber the line number, 1 being the first line of the file. * @return The line indicated. Does not include the newline at the end * of the file. Returns {@code null} if it does not exist, * or if there was an IO exception. */ public String getLine(int lineNumber) { return getSourceFile().getLine(lineNumber); } /** * Get a region around the indicated line number. The exact definition of a * region is implementation specific, but it must contain the line indicated * by the line number. A region must not start or end by a carriage return. * * @param lineNumber the line number, 1 being the first line of the file. * @return The line indicated. Returns {@code null} if

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>> * <li>{@code qualified.name = function() ...}</li> * <li>{@code var name2 = function name1() ...}</li> * <li>{@code qualified.name2 = function name1() ...}</li> * </ul> * In two last cases with named anonymous functions, the second name is * returned (the variable of qualified name). * * @param n a node whose type is {@link Token#FUNCTION} * @return the function's name, or {@code null} if it has no name */ static String getFunctionName(Node n) { Node parent = n.getParent(); String name = n.getFirstChild().getString(); switch (parent.getType()) { case Token.NAME: // var name = function() ... // var name2 = function name1() ... return parent.getString(); case Token.ASSIGN: // qualified.name = function() ... // qualified.name2 = function name1() ... return parent.getFirstChild().getQualifiedName(); default: // function name() ... return name != null && name.length() != 0 ? name : null; } } /** * Returns true if this is an immutable value. */ static boolean isImmutableValue(Node n) { switch (n.getType()) { case Token.STRING: case Token.NUMBER: case Token.NULL: case Token.TRUE: case Token.FALSE: case Token.VOID: return true; case Token.NEG: return isImmutableValue(n.getFirstChild()); case Token.NAME: String name = n.getString(); // We assume here that programs don't change the value of the keyword // undefined to something other than the value undefined. return "undefined".equals(name) || "Infinity".equals(name) || "NaN".equals(name); } return false; } /** * Returns true if this is a literal value. We define a literal value * as any node that evaluates to the same thing regardless of when or * where it is evaluated. So /xyz/ and [3, 5] are literals, but * function() { return a; } is not. */ static boolean isLiteralValue(Node n) { // TODO(

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>nicksantos): Refine this function to catch more literals. switch (n.getType()) { case Token.ARRAYLIT: case Token.OBJECTLIT: case Token.REGEXP: // Return true only if all children are const. for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (!isLiteralValue(child)) { return false; } } return true; default: return isImmutableValue(n); } } /** * Determines whether the given value may be assigned to a define. * * @param val The value being assigned. * @param defines The list of names of existing defines. */ static boolean isValidDefineValue(Node val, Set<String> defines) { switch (val.getType()) { case Token.STRING: case Token.NUMBER: case Token.TRUE: case Token.FALSE: return true; // Single operators are valid if the child is valid. case Token.BITAND: case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.NOT: case Token.NEG: return isValidDefineValue(val.getFirstChild(), defines); // Names are valid if and only if they are defines themselves. case Token.NAME: case Token.GETPROP: if (val.isQualifiedName()) { return defines.contains(val.getQualifiedName()); } } return false; } /** * Returns whether this a BLOCK node with no children. * * @param block The node. */ static boolean isEmptyBlock(Node block) { if (block.getType() != Token.BLOCK) { return false; } for (Node n = block.getFirstChild(); n != null; n = n.getNext()) { if (n.getType() != Token.EMPTY) { return false; } } return true; } /** * A "simple" operator is one whose children are expressions, * has no direct side-effects (unlike '+='), and has no * conditional aspects (unlike '||'). */ static boolean isSimpleOperatorType(int type) { switch (type) { case Token.ADD: case Token.BIT

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>()) { // other side-effect free statements and expressions case Token.AND: case Token.BLOCK: case Token.EXPR_RESULT: case Token.HOOK: case Token.IF: case Token.IN: case Token.LP: case Token.NUMBER: case Token.OR: case Token.THIS: case Token.TRUE: case Token.FALSE: case Token.NULL: case Token.STRING: case Token.SWITCH: case Token.TRY: case Token.EMPTY: break; // Throws are by definition side effects case Token.THROW: return true; case Token.OBJECTLIT: case Token.ARRAYLIT: case Token.REGEXP: if (checkForNewObjects) { return true; } break; case Token.VAR: // empty var statement (no declaration) case Token.NAME: // variable by itself if (n.getFirstChild() != null) return true; break; case Token.FUNCTION: // Anonymous functions don't have side-effects, but named ones // change the namespace. Therefore, we check if the function has // a name. Either way, we don't need to check the children, since // they aren't executed at declaration time. // return !isFunctionAnonymous(n); case Token.NEW: { if (checkForNewObjects) { return true; } // calls to constructors that have no side effects have the // no side effect property set. if (n.isNoSideEffectsCall()) { break; } // certain constructors are certified side effect free Node constructor = n.getFirstChild(); if (Token.NAME == constructor.getType()) { String className = constructor.getString(); if (CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(className)) { // loop below will see if the constructor parameters have // side-effects break; } } else { // the constructor could also be an expression like // new (useArray ? Object : Array)(); } } return true; case Token.CALL: // calls to functions that have no side effects have the no // side effect property set. if (n.isNoSideEffectsCall()) { // loop below will see

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> if the function parameters have // side-effects break; } return true; default: if (isSimpleOperatorType(n.getType())) break; if (isAssignmentOp(n)) { // Assignments will have side effects if // a) The RHS has side effects, or // b) The LHS has side effects, or // c) A name on the LHS will exist beyond the life of this statement. if (checkForStateChangeHelper( n.getFirstChild(), checkForNewObjects) || checkForStateChangeHelper( n.getLastChild(), checkForNewObjects)) { return true; } Node current = n.getFirstChild(); for (; current.getType() == Token.GETPROP || current.getType() == Token.GETELEM; current = current.getFirstChild()) { } return !(isLiteralValue(current) || current.getType() == Token.FUNCTION); } return true; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (checkForStateChangeHelper(c, checkForNewObjects)) { return true; } } return false; } /** * Do calls to this constructor have side effects? * * @param callNode - construtor call node */ static boolean constructorCallHasSideEffects(Node callNode) { Preconditions.checkArgument( callNode.getType() == Token.NEW, "Expected NEW node, got " + Token.name(callNode.getType())); if (callNode.isNoSideEffectsCall()) { return false; } Node nameNode = callNode.getFirstChild(); if (nameNode.getType() == Token.NAME && CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(nameNode.getString())) { return false; } return true; } /** * Returns true if calls to this function have side effects. * * @param callNode - function call node */ static boolean functionCallHasSideEffects(Node callNode) { Preconditions.checkArgument( callNode.getType() == Token.CALL, "Expected CALL node, got " + Token.name(callNode.getType())); if (callNode.isNoSideEffectsCall()) {

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> return false; } Node nameNode = callNode.getFirstChild(); // Built-in functions with no side effects. if (nameNode.getType() == Token.NAME) { String name = nameNode.getString(); if (name.equals("String")) { return false; } } // Functions in the "Math" namespace have no side effects. if (nameNode.getType() == Token.GETPROP && nameNode.getFirstChild().getType() == Token.NAME) { String namespaceName = nameNode.getFirstChild().getString(); if (namespaceName.equals("Math")) { return false; } } return true; } /** * Returns true if the current node's type implies side effects. * * This is a non-recursive version of the may have side effects * check; used to check wherever the current node's type is one of * the reason's why a subtree has side effects. */ static boolean nodeTypeMayHaveSideEffects(Node n) { if (NodeUtil.isAssignmentOp(n)) { return true; } switch(n.getType()) { case Token.CALL: case Token.DELPROP: case Token.NEW: case Token.DEC: case Token.INC: case Token.THROW: return true; case Token.NAME: // A variable definition. return n.hasChildren(); default: return false; } } /** * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n) { Set<String> emptySet = Collections.emptySet(); return canBeSideEffected(n, emptySet); } /** * @param knownConstants A set of names known to be constant value at * node 'n' (such as locals that are last written before n can execute). * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n, Set<String> knownConstants) { switch (n.getType()) { case Token.CALL: case Token.NEW: // Function calls or constructor can reference changed values. // TODO(

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>johnlenz): Add some mechanism for determining that functions // are unaffected by side effects. return true; case Token.NAME: // Non-constant names values may have been changed. return !NodeUtil.isConstantName(n) && !knownConstants.contains(n.getString()); // Properties on constant NAMEs can still be side-effected. case Token.GETPROP: case Token.GETELEM: return true; case Token.FUNCTION: // Anonymous functions definitions are not changed by side-effects, // and named functions are not part of expressions. Preconditions.checkState(NodeUtil.isAnonymousFunction(n)); return false; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (canBeSideEffected(c, knownConstants)) { return true; } } return false; } /* * 0 comma , * 1 assignment = += -= *= /= %= <<= >>= >>>= &= ^= |= * 2 conditional ?: * 3 logical-or || * 4 logical-and && * 5 bitwise-or | * 6 bitwise-xor ^ * 7 bitwise-and & * 8 equality == != * 9 relational < <= > >= * 10 bitwise shift << >> >>> * 11 addition/subtraction + - * 12 multiply/divide * / % * 13 negation/increment ! ~ - ++ -- * 14 call, member () [] . */ static int precedence(int type) { switch (type) { case Token.COMMA: return 0; case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN: return 1; case Token.HOOK: return 2; // ?: operator case Token.OR: return

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> node? */ static boolean isGet(Node n) { return n.getType() == Token.GETPROP || n.getType() == Token.GETELEM; } /** * Is this a GETPROP node? */ static boolean isGetProp(Node n) { return n.getType() == Token.GETPROP; } /** * Is this a NAME node? */ static boolean isName(Node n) { return n.getType() == Token.NAME; } /** * Is this a NEW node? */ static boolean isNew(Node n) { return n.getType() == Token.NEW; } /** * Is this a VAR node? */ static boolean isVar(Node n) { return n.getType() == Token.VAR; } /** * Is this node the name of a variable being declared? * * @param n The node * @return True if {@code n} is NAME and {@code parent} is VAR */ static boolean isVarDeclaration(Node n) { // There is no need to verify that parent != null because a NAME node // always has a parent in a valid parse tree. return n.getType() == Token.NAME && n.getParent().getType() == Token.VAR; } /** * For an assignment or variable declaration get the assigned value. * @return The value node representing the new value. */ static Node getAssignedValue(Node n) { Preconditions.checkState(isName(n)); Node parent = n.getParent(); if (isVar(parent)) { return n.getFirstChild(); } else if (isAssign(parent) && parent.getFirstChild() == n) { return n.getNext(); } else { return null; } } /** * Is this a STRING node? */ static boolean isString(Node n) { return n.getType() == Token.STRING; } /** * Is this node an assignment expression statement? * * @param n The node * @return True if {@code n} is EXPR_RESULT and {@code n}'s * first child is ASSIGN */ static boolean isExprAssign(Node n) { return n.getType() == Token

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>.EXPR_RESULT && n.getFirstChild().getType() == Token.ASSIGN; } /** * Is this an ASSIGN node? */ static boolean isAssign(Node n) { return n.getType() == Token.ASSIGN; } /** * Is this node a call expression statement? * * @param n The node * @return True if {@code n} is EXPR_RESULT and {@code n}'s * first child is CALL */ static boolean isExprCall(Node n) { return n.getType() == Token.EXPR_RESULT && n.getFirstChild().getType() == Token.CALL; } /** * @return Whether the node represents a FOR-IN loop. */ static boolean isForIn(Node n) { return n.getType() == Token.FOR && n.getChildCount() == 3; } /** * Determines whether the given node is a FOR, DO, or WHILE node. */ static boolean isLoopStructure(Node n) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: return true; default: return false; } } /** * @param n The node to inspect. * @return If the node, is a FOR, WHILE, or DO, it returns the node for * the code BLOCK, null otherwise. */ static Node getLoopCodeBlock(Node n) { switch (n.getType()) { case Token.FOR: case Token.WHILE: return n.getLastChild(); case Token.DO: return n.getFirstChild(); default: return null; } } /** * Determines whether the given node is a FOR, DO, WHILE, WITH, or IF node. */ static boolean isControlStructure(Node n) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: case Token.WITH: case Token.IF: case Token.LABEL: case Token.TRY: case Token.CATCH: case Token.SWITCH: case Token.CASE: case Token.DEFAULT: return true; default: return false; }

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> } /** * Determines whether the given node is code node for FOR, DO, * WHILE, WITH, or IF node. */ static boolean isControlStructureCodeBlock(Node parent, Node n) { switch (parent.getType()) { case Token.FOR: case Token.WHILE: case Token.LABEL: case Token.WITH: return parent.getLastChild() == n; case Token.DO: return parent.getFirstChild() == n; case Token.IF: return parent.getFirstChild() != n; case Token.TRY: return parent.getFirstChild() == n || parent.getLastChild() == n; case Token.CATCH: return parent.getLastChild() == n; case Token.SWITCH: case Token.CASE: return parent.getFirstChild() != n; case Token.DEFAULT: return true; default: Preconditions.checkState(isControlStructure(parent)); return false; } } /** * Gets the condition of an ON_TRUE / ON_FALSE CFG edge. * @param n a node with an outgoing conditional CFG edge * @return the condition node or null if the condition is not obviously a node */ static Node getConditionExpression(Node n) { switch (n.getType()) { case Token.IF: case Token.WHILE: return n.getFirstChild(); case Token.DO: return n.getLastChild(); case Token.FOR: switch (n.getChildCount()) { case 3: return null; case 4: return n.getFirstChild().getNext(); } throw new IllegalArgumentException("malformed 'for' statement " + n); case Token.CASE: return null; } throw new IllegalArgumentException(n + " does not have a condition."); } /** * @return Whether the node is of a type that contain other statements. */ static boolean isStatementBlock(Node n) { return n.getType() == Token.SCRIPT || n.getType() == Token.BLOCK; } /** * @return Whether the node is used as a statement. */ static boolean isStatement(Node n) { Node parent = n.getParent(); // It is not possible to determine definitely if a node is a statement // or

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> not if it is not part of the AST. A FUNCTION node, for instance, // is either part of an expression (as a anonymous function) or as // a statement. Preconditions.checkState(parent != null); switch (parent.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.LABEL: return true; default: return false; } } /** Whether the node is part of a switch statement. */ static boolean isSwitchCase(Node n) { return n.getType() == Token.CASE || n.getType() == Token.DEFAULT; } /** * @return Whether the name is a reference to a variable, function or * function parameter (not a label or a empty anonymous function name). */ static boolean isReferenceName(Node n) { return isName(n) && !n.getString().isEmpty(); } /** @return Whether the node is a label name. */ static boolean isLabelName(Node n) { return (n != null && n.getType() == Token.LABEL_NAME); } /** Whether the child node is the FINALLY block of a try. */ static boolean isTryFinallyNode(Node parent, Node child) { return parent.getType() == Token.TRY && parent.getChildCount() == 3 && child == parent.getLastChild(); } /** Safely remove children while maintaining a valid node structure. */ static void removeChild(Node parent, Node node) { // Node parent = node.getParent(); if (isStatementBlock(parent) || isSwitchCase(node) || isTryFinallyNode(parent, node)) { // A statement in a block can simply be removed. parent.removeChild(node); } else if (parent.getType() == Token.VAR) { if (parent.hasMoreThanOneChild()) { parent.removeChild(node); } else { // Remove the node from the parent, so it can be reused. parent.removeChild(node); // This would leave an empty VAR, remove the VAR itself. removeChild(parent.getParent(), parent); } } else if (node.getType() == Token.BLOCK) { // Simply empty the block. This maintains source location and // "synthetic"-

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>ness. node.detachChildren(); } else if (parent.getType() == Token.LABEL && node == parent.getLastChild()) { // Remove the node from the parent, so it can be reused. parent.removeChild(node); // A LABEL without children can not be referred to, remove it. removeChild(parent.getParent(), parent); } else if (parent.getType() == Token.FOR && parent.getChildCount() == 4) { // Only Token.FOR can have an Token.EMPTY other control structure // need something for the condition. Others need to be replaced // or the structure removed. parent.replaceChild(node, new Node(Token.EMPTY)); } else { throw new IllegalStateException("Invalid attempt to remove node: " + node.toString() + " of "+ parent.toString()); } } /** * Merge a block with its parent block. * @return Whether the block was removed. */ static boolean tryMergeBlock(Node block) { Preconditions.checkState(block.getType() == Token.BLOCK); Node parent = block.getParent(); // Try to remove the block if its parent is a block/script or if its // parent is label and it has exactly one child. if (NodeUtil.isStatementBlock(parent)) { Node previous = block; while (block.hasChildren()) { Node child = block.removeFirstChild(); parent.addChildAfter(child, previous); previous = child; } parent.removeChild(block); return true; } else if (parent.getType() == Token.LABEL && block.hasOneChild()) { parent.replaceChild(block, block.removeFirstChild()); return true; } else { return false; } } /** * Is this a CALL node? */ static boolean isCall(Node n) { return n.getType() == Token.CALL; } /** * Is this a FUNCTION node? */ static boolean isFunction(Node n) { return n.getType() == Token.FUNCTION; } /** * Return a BLOCK node for the given FUNCTION node. */ static Node getFunctionBody(Node fn) { Preconditions.checkArgument(isFunction(fn)); return fn.getLastChild(); }

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> static boolean isFunctionAnonymous(Node n) { return !isStatement(n); } /** * Determines if a function takes a variable number of arguments by * looking for references to the "arguments" var_args object. */ static boolean isVarArgsFunction(Node function) { Preconditions.checkArgument(isFunction(function)); return NodeUtil.isNameReferenced( function.getLastChild(), "arguments", Predicates.<Node>not(new NodeUtil.MatchNodeType(Token.FUNCTION))); } /** * @return Whether node is a call to methodName. * a.f(...) * a['f'](...) */ static boolean isObjectCallMethod(Node callNode, String methodName) { if (callNode.getType() == Token.CALL) { Node functionIndentifyingExpression = callNode.getFirstChild(); if (NodeUtil.isGet(functionIndentifyingExpression)) { Node last = functionIndentifyingExpression.getLastChild(); if (last != null && last.getType() == Token.STRING) { String propName = last.getString(); return (propName.equals(methodName)); } } } return false; } /** * @return Whether the callNode represents an expression in the form of: * x.call(...) * x['call'](...) */ static boolean isFunctionObjectCall(Node callNode) { return isObjectCallMethod(callNode, "call"); } /** * @return Whether the callNode represents an expression in the form of: * x.apply(...) * x['apply'](...) */ static boolean isFunctionObjectApply(Node callNode) { return isObjectCallMethod(callNode, "apply"); } /** * @return Whether the callNode represents an expression in the form of: * x.call(...) * x['call'](...) * where x is a NAME node. */ static boolean isSimpleFunctionObjectCall(Node callNode) { if (isFunctionObjectCall(callNode)) { if (callNode.getFirstChild().getFirstChild().getType() == Token.NAME) { return true; } } return false; } /** * Determines whether this node is strictly on the left hand side of

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> an assign * or var initialization. Notably, this does not include all L-values, only * statements where the node is used only as an L-value. * * @param n The node * @param parent Parent of the node * @return True if n is the left hand of an assign */ static boolean isLhs(Node n, Node parent) { return (parent.getType() == Token.ASSIGN && parent.getFirstChild() == n) || parent.getType() == Token.VAR; } /** * Determines whether a node represents an object literal key * (e.g. key1 in {key1: value1, key2: value2}). * * @param node A node * @param parent The node's parent */ static boolean isObjectLitKey(Node node, Node parent) { if (node.getType() == Token.STRING && parent.getType() == Token.OBJECTLIT) { int index = 0; for (Node current = parent.getFirstChild(); current != null; current = current.getNext()) { if (current == node) { return index % 2 == 0; } index++; } } return false; } /** * Converts an operator's token value (see {@link Token}) to a string * representation. * * @param operator the operator's token value to convert * @return the string representation or {@code null} if the token value is * not an operator */ static String opToStr(int operator) { switch (operator) { case Token.BITOR: return "|"; case Token.OR: return "||"; case Token.BITXOR: return "^"; case Token.AND: return "&&"; case Token.BITAND: return "&"; case Token.SHEQ: return "==="; case Token.EQ: return "=="; case Token.NOT: return "!"; case Token.NE: return "!="; case Token.SHNE: return "!=="; case Token.LSH: return "<<"; case Token.IN: return "in"; case Token.LE: return "<="; case Token.LT: return "<"; case Token.URSH: return ">>>"; case Token

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> */ static boolean containsType(Node node, int type, Predicate<Node> traverseChildrenPred) { return has(node, new MatchNodeType(type), traverseChildrenPred); } /** * @return true if n or any of its children are of the specified type */ static boolean containsType(Node node, int type) { return containsType(node, type, Predicates.<Node>alwaysTrue()); } /** * Given a node tree, finds all the VAR declarations in that tree that are * not in an inner scope. Then adds a new VAR node at the top of the current * scope that redeclares them, if necessary. */ static void redeclareVarsInsideBranch(Node branch) { Collection<Node> vars = getVarsDeclaredInBranch(branch); if (vars.isEmpty()) { return; } Node parent = getAddingRoot(branch); for (Node nameNode : vars) { Node var = new Node( Token.VAR, Node.newString(Token.NAME, nameNode.getString())); copyNameAnnotations(nameNode, var.getFirstChild()); parent.addChildToFront(var); } } /** * Copy any annotations that follow a named value. * @param source * @param destination */ static void copyNameAnnotations(Node source, Node destination) { if (source.getBooleanProp(Node.IS_CONSTANT_NAME)) { destination.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } /** * Gets a Node at the top of the current scope where we can add new var * declarations as children. */ private static Node getAddingRoot(Node n) { Node addingRoot = null; Node ancestor = n; while (null != (ancestor = ancestor.getParent())) { int type = ancestor.getType(); if (type == Token.SCRIPT) { addingRoot = ancestor; break; } else if (type == Token.FUNCTION) { addingRoot = ancestor.getLastChild(); break; } } // make sure that the adding root looks ok Preconditions.checkState(addingRoot.getType() == Token.BLOCK || addingRoot.getType() == Token.SCRIPT); Preconditions.checkState(addingRoot.getFirstChild()

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> == null || addingRoot.getFirstChild().getType() != Token.SCRIPT); return addingRoot; } /** Creates function name(params_0, ..., params_n) { body }. */ public static FunctionNode newFunctionNode(String name, List<Node> params, Node body, int lineno, int charno) { Node parameterParen = new Node(Token.LP, lineno, charno); for (Node param : params) { parameterParen.addChildToBack(param); } FunctionNode function = new FunctionNode(name, lineno, charno); function.addChildrenToBack( Node.newString(Token.NAME, name, lineno, charno)); function.addChildToBack(parameterParen); function.addChildToBack(body); return function; } /** * Creates a node representing a qualified name. * * @param name A qualified name (e.g. "foo" or "foo.bar.baz") * @param lineno The source line offset. * @param charno The source character offset from start of the line. * @return A NAME or GETPROP node */ public static Node newQualifiedNameNode(String name, int lineno, int charno) { int endPos = name.indexOf('.'); if (endPos == -1) { return Node.newString(Token.NAME, name, lineno, charno); } Node node = Node.newString(Token.NAME, name.substring(0, endPos), lineno, charno); int startPos; do { startPos = endPos + 1; endPos = name.indexOf('.', startPos); String part = (endPos == -1 ? name.substring(startPos) : name.substring(startPos, endPos)); node = new Node(Token.GETPROP, node, Node.newString(Token.STRING, part, lineno, charno), lineno, charno); } while (endPos != -1); return node; } /** * Creates a node representing a qualified name, copying over the source * location information from the basis node and assigning the given original * name to the node. * * @param name A qualified name (e.g. "foo" or "foo.

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> { vars.put(name, n); } } } } } /** * Retrieves vars declared in the current node tree, excluding descent scopes. */ public static Collection<Node> getVarsDeclaredInBranch(Node root) { VarCollector collector = new VarCollector(); visitPreOrder( root, collector, Predicates.<Node>not(new NodeUtil.MatchNodeType(Token.FUNCTION))); return collector.vars.values(); } /** * @return {@code true} if the node an assignment to a prototype property of * some constructor. */ static boolean isPrototypePropertyDeclaration(Node n) { if (!NodeUtil.isExprAssign(n)) { return false; } return isPrototypeProperty(n.getFirstChild().getFirstChild()); } static boolean isPrototypeProperty(Node n) { String lhsString = n.getQualifiedName(); if (lhsString == null) { return false; } int prototypeIdx = lhsString.indexOf(".prototype."); return prototypeIdx != -1; } /** * @return The class name part of a qualified prototype name. */ static Node getPrototypeClassName(Node qName) { Node cur = qName; while (isGetProp(cur)) { if (cur.getLastChild().getString().equals("prototype")) { return cur.getFirstChild(); } else { cur = cur.getFirstChild(); } } return null; } /** * @return The string property name part of a qualified prototype name. */ static String getPrototypePropertyName(Node qName) { String qNameStr = qName.getQualifiedName(); int prototypeIdx = qNameStr.lastIndexOf(".prototype."); int memberIndex = prototypeIdx + ".prototype".length() + 1; return qNameStr.substring(memberIndex); } /** * Create a node for an empty result expression: * "void 0" */ static Node newUndefinedNode() { // TODO(johnlenz): Why this instead of the more common "undefined"? return new Node(Token.VOID, Node.newNumber(0)); } /** * Create a VAR node containing the given name and initial value expression. */ static Node newVarNode(

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>(Node node, String name) { return getCount(node, new MatchNameNode(name) ); } /** * @return Whether the predicate is true for the node or any of its children. */ static boolean has(Node node, Predicate<Node> pred, Predicate<Node> traverseChildrenPred) { if (pred.apply(node)) { return true; } if (!traverseChildrenPred.apply(node)) { return false; } for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { if (has(c, pred, traverseChildrenPred)) { return true; } } return false; } /** * @return The number of times the the predicate is true for the node * or any of its children. */ static int getCount(Node n, Predicate<Node> pred) { int total = 0; if (pred.apply(n)) { total++; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { total += getCount(c, pred); } return total; } /** * Interface for use with the visit method. * @see #visit */ static interface Visitor { void visit(Node node); } /** * A pre-order traversal, calling Vistor.visit for each child matching * the predicate. */ static void visitPreOrder(Node node, Visitor vistor, Predicate<Node> traverseChildrenPred) { vistor.visit(node); if (traverseChildrenPred.apply(node)) { for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { visitPreOrder(c, vistor, traverseChildrenPred); } } } /** * A post-order traversal, calling Vistor.visit for each child matching * the predicate. */ static void visitPostOrder(Node node, Visitor vistor, Predicate<Node> traverseChildrenPred) { if (traverseChildrenPred.apply(node)) { for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { visitPostOrder(c, vistor, traverseChildrenPred);

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> } } vistor.visit(node); } /** * @return Whether a TRY node has a finally block. */ static boolean hasFinally(Node n) { Preconditions.checkArgument(n.getType() == Token.TRY); return n.getChildCount() == 3; } /** * @return The BLOCK node containing the CATCH node (if any) * of a TRY. */ static Node getCatchBlock(Node n) { Preconditions.checkArgument(n.getType() == Token.TRY); return n.getFirstChild().getNext(); } /** * @return Whether BLOCK (from a TRY node) contains a CATCH. * @see NodeUtil#getCatchBlock */ static boolean hasCatchHandler(Node n) { Preconditions.checkArgument(n.getType() == Token.BLOCK); return n.hasChildren() && n.getFirstChild().getType() == Token.CATCH; } /** * @param fnNode The function. * @return The Node containing the Function parameters. */ static Node getFnParameters(Node fnNode) { // Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ] Preconditions.checkArgument(fnNode.getType() == Token.FUNCTION); return fnNode.getFirstChild().getNext(); } /** * Returns true if a name node represents a constant variable. * * <p>Determining whether a variable is constant has three steps: * <ol> * <li>In CodingConventionAnnotator, any name that matches the * {@link CodingConvention#isConstant(String)} is annotated with an * IS_CONSTANT_NAME property. * <li>The normalize pass renames any variable with the IS_CONSTANT_NAME * annotation and that is initialized to a constant value with * a variable name inlucding $$constant. * <li>Return true here if the variable includes $$constant in its name. * </ol> * * @param node A NAME or STRING node * @return True if the variable is constant */ static boolean isConstantName(Node node) { return node.getBooleanProp(Node.IS_CONSTANT_NAME); } /** * @param nameNode A name node * @

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> path through // the code identical to how it's been for years. this.outputCharsetEncoder = null; } else { this.outputCharsetEncoder = outputCharset.newEncoder(); } } CodeGenerator(CodeConsumer consumer, Charset outputCharset) { this(consumer, outputCharset, true); } CodeGenerator(CodeConsumer consumer) { this(consumer, null, false); } void add(String str) { cc.add(str); } private void addIdentifier(String identifier) { cc.addIdentifier(identifierEscape(identifier)); } void add(Node n) { add(n, Context.OTHER); } void add(Node n, Context context) { if (!cc.continueProcessing()) { return; } int type = n.getType(); String opstr = NodeUtil.opToStr(type); int childCount = n.getChildCount(); Node first = n.getFirstChild(); Node last = n.getLastChild(); // Handle all binary operators if (opstr != null && first != last) { Preconditions.checkState(childCount == 2); int p = NodeUtil.precedence(type); addLeftExpr(first, p, context); cc.addOp(opstr, true); // For right-hand-side of operations, only pass context if it's // the IN_FOR_INIT_CLAUSE one. Context rhsContext = getContextForNoInOperator(context); // Handle associativity. // e.g. if the parse tree is a * (b * c), // we can simply generate a * b * c. if (last.getType() == type && NodeUtil.isAssociative(type)) { addExpr(last, p, rhsContext); } else if (NodeUtil.isAssignmentOp(n) && NodeUtil.isAssignmentOp(last)) { // Assignments are the only right-associative binary operators addExpr(last, p, rhsContext); } else { addExpr(last, p + 1, rhsContext); } return; } cc.startSourceMapping(n); switch (type) { case Token.TRY: { Preconditions.checkState(first.getNext().getType() ==

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> Token.BLOCK && first.getNext().getChildCount() <= 1); Preconditions.checkState(childCount >= 2 && childCount <= 3); add("try"); add(first, Context.PRESERVE_BLOCK); // second child contains the catch block, or nothing if there // isn't a catch block Node catchblock = first.getNext().getFirstChild(); if (catchblock != null) { add(catchblock); } if (childCount == 3) { add("finally"); add(last, Context.PRESERVE_BLOCK); } break; } case Token.CATCH: Preconditions.checkState(childCount == 3); if (first.getNext().getType() != Token.EMPTY) { throw new Error("Catch conditions not suppored because I think" + " that it may be a netscape only feature."); } add("catch("); add(first); add(")"); add(last, Context.PRESERVE_BLOCK); break; case Token.THROW: Preconditions.checkState(childCount == 1); add("throw"); add(first); // Must have a ';' after a throw statement, otherwise safari can't // parse this. cc.endStatement(true); break; case Token.RETURN: add("return"); if (childCount == 1) { add(first); } else { Preconditions.checkState(childCount == 0); } cc.endStatement(); break; case Token.VAR: if (first != null) { add("var "); addList(first, false, getContextForNoInOperator(context)); } break; case Token.LABEL_NAME: Preconditions.checkState(!n.getString().isEmpty()); addIdentifier(n.getString()); break; case Token.NAME: if (first == null || first.getType() == Token.EMPTY) { addIdentifier(n.getString()); } else { Preconditions.checkState(childCount == 1); addIdentifier(n.getString()); cc.addOp("=", true); if (first.getType() == Token.COMMA) { addExpr(first, NodeUtil.precedence(Token.ASSIGN)); } else

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> { // Add expression, consider nearby code at lowest level of // precedence. addExpr(first, 0, getContextForNoInOperator(context)); } } break; case Token.ARRAYLIT: add("["); addList(first, (int[]) n.getProp(Node.SKIP_INDEXES_PROP)); add("]"); break; case Token.LP: add("("); addList(first); add(")"); break; case Token.COMMA: addList(first, false, context); break; case Token.NUMBER: Preconditions.checkState(childCount == 0); cc.addNumber(n.getDouble()); break; case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: case Token.NEG: { // All of these unary operators are right-associative Preconditions.checkState(childCount == 1); cc.addOp(NodeUtil.opToStrNoFail(type), false); addExpr(first, NodeUtil.precedence(type)); break; } case Token.HOOK: { Preconditions.checkState(childCount == 3); int p = NodeUtil.precedence(type); addLeftExpr(first, p + 1, context); cc.addOp("?", true); addExpr(first.getNext(), p); cc.addOp(":", true); addExpr(last, p); break; } case Token.REGEXP: if (first.getType() != Token.STRING || last.getType() != Token.STRING) { throw new Error("Expected children to be strings"); } String regexp = regexpEscape(first.getString(), outputCharsetEncoder); // I only use one .add because whitespace matters if (childCount == 2) { add(regexp + last.getString()); } else { Preconditions.checkState(childCount == 1); add(regexp); } break; case Token.GET_REF: add(first); break; case Token.REF_SPECIAL: Preconditions.checkState(childCount == 1); add(first); add("."); add((String) n.

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>getProp(Node.NAME_PROP)); break; case Token.FUNCTION: Preconditions.checkState(childCount == 3); boolean funcNeedsParens = (context == Context.START_OF_EXPR); if (funcNeedsParens) { add("("); } add("function"); add(first); add(first.getNext()); add(last, Context.PRESERVE_BLOCK); cc.endFunction(context == Context.STATEMENT); if (funcNeedsParens) { add(")"); } break; case Token.SCRIPT: case Token.BLOCK: { boolean stripBlock = n.isSyntheticBlock() || ((context != Context.PRESERVE_BLOCK) && (n.getChildCount() < 2)); if (!stripBlock) { cc.beginBlock(); } for (Node c = first; c != null; c = c.getNext()) { add(c, Context.STATEMENT); // VAR doesn't include ';' since it gets used in expressions if (c.getType() == Token.VAR) { cc.endStatement(); } if (c.getType() == Token.FUNCTION) { cc.maybeLineBreak(); } // Prefer to break lines in between top-level statements // because top level statements are more homogeneous. if (type == Token.SCRIPT) { cc.notePreferredLineBreak(); } } if (!stripBlock) { cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT)); } break; } case Token.FOR: if (childCount == 4) { add("for("); if (first.getType() == Token.VAR) { add(first, Context.IN_FOR_INIT_CLAUSE); } else { addExpr(first, 0, Context.IN_FOR_INIT_CLAUSE); } add(";"); add(first.getNext()); add(";"); add(first.getNext().getNext()); add(")"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), false); } else { Preconditions.checkState(childCount == 3); add("for("); add(first

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>); add("in"); add(first.getNext()); add(")"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), false); } break; case Token.DO: Preconditions.checkState(childCount == 2); add("do"); addNonEmptyExpression(first, Context.OTHER, false); add("while("); add(last); add(")"); cc.endStatement(); break; case Token.WHILE: Preconditions.checkState(childCount == 2); add("while("); add(first); add(")"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), false); break; case Token.EMPTY: Preconditions.checkState(childCount == 0); break; case Token.GETPROP: { Preconditions.checkState(childCount == 2); Preconditions.checkState(last.getType() == Token.STRING); boolean needsParens = (first.getType() == Token.NUMBER); if (needsParens) { add("("); } addLeftExpr(first, NodeUtil.precedence(type), context); if (needsParens) { add(")"); } add("."); addIdentifier(last.getString()); break; } case Token.GETELEM: Preconditions.checkState(childCount == 2); addLeftExpr(first, NodeUtil.precedence(type), context); add("["); add(first.getNext()); add("]"); break; case Token.WITH: Preconditions.checkState(childCount == 2); add("with("); add(first); add(")"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), false); break; case Token.INC: case Token.DEC: { Preconditions.checkState(childCount == 1); String o = type == Token.INC ? "++" : "--"; int postProp = n.getIntProp(Node.INCRDECR_PROP, 0); // A non-zero post-prop value indicates a post inc/dec, default of zero // is a pre-inc/dec. if (postProp != 0) {

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> addLeftExpr(first, NodeUtil.precedence(type), context); cc.addOp(o, false); } else { cc.addOp(o, false); add(first); } break; } case Token.CALL: // If the left hand side of the call is a direct reference to eval, // then it must have a DIRECT_EVAL annotation. If it does not, then // that means it was originally an indirect call to eval, and that // indirectness must be preserved. if (first.getType() == Token.NAME && "eval".equals(first.getString()) && !first.getBooleanProp(Node.DIRECT_EVAL)) { add("(0,eval)"); } else { addLeftExpr(first, NodeUtil.precedence(type), context); } add("("); addList(first.getNext()); add(")"); break; case Token.IF: boolean hasElse = childCount == 3; boolean ambiguousElseClause = context == Context.BEFORE_DANGLING_ELSE && !hasElse; if (ambiguousElseClause) { cc.beginBlock(); } add("if("); add(first); add(")"); if (hasElse) { addNonEmptyExpression( first.getNext(), Context.BEFORE_DANGLING_ELSE, false); add("else"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), false); } else { addNonEmptyExpression(first.getNext(), Context.OTHER, false); Preconditions.checkState(childCount == 2); } if (ambiguousElseClause) { cc.endBlock(); } break; case Token.NULL: case Token.THIS: case Token.FALSE: case Token.TRUE: Preconditions.checkState(childCount == 0); add(Node.tokenToName(type)); break; case Token.CONTINUE: Preconditions.checkState(childCount <= 1); add("continue"); if (childCount == 1) { if (first.getType() != Token.LABEL_NAME && validation) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(" "); add(

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>first); } cc.endStatement(); break; case Token.DEBUGGER: Preconditions.checkState(childCount == 0); add("debugger"); cc.endStatement(); break; case Token.BREAK: Preconditions.checkState(childCount <= 1); add("break"); if (childCount == 1) { if (first.getType() != Token.LABEL_NAME && validation) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(" "); add(first); } cc.endStatement(); break; case Token.EXPR_VOID: case Token.EXPR_RESULT: if (type == Token.EXPR_VOID && validation) { throw new Error("Unexpected EXPR_VOID. Should be EXPR_RESULT."); } Preconditions.checkState(childCount == 1); add(first, Context.START_OF_EXPR); cc.endStatement(); break; case Token.NEW: add("new "); int precedence = NodeUtil.precedence(type); // If the first child contains a CALL, then claim higher precedence // to force parens. Otherwise, when parsed, NEW will bind to the // first viable parens if (NodeUtil.containsCall(first)) { precedence = NodeUtil.precedence(first.getType()) + 1; } addExpr(first, precedence); // '()' is optional when no arguments are present Node next = first.getNext(); if (next != null) { add("("); addList(next); add(")"); } break; case Token.STRING: Preconditions.checkState(childCount == 0); add(jsString(n.getString(), outputCharsetEncoder)); break; case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { Preconditions.checkState(childCount % 2 == 0); boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>().getNext()) { if (c != first) { cc.listSeparator(); } // Object literal property names don't have to be quoted if they are // not JavaScript keywords if (c.getType() == Token.STRING && !TokenStream.isKeyword(c.getString()) && TokenStream.isJSIdentifier(c.getString()) && // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(c.getString())) { add(c.getString()); } else { addExpr(c, 1); } add(":"); addExpr(c.getNext(), 1); } add("}"); if (needsParens) { add(")"); } break; } case Token.SWITCH: add("switch("); add(first); add(")"); cc.beginBlock(); addAllSiblings(first.getNext()); cc.endBlock(context == Context.STATEMENT); break; case Token.CASE: Preconditions.checkState(childCount == 2); add("case "); add(first); addCaseBody(last); break; case Token.DEFAULT: Preconditions.checkState(childCount == 1); add("default"); addCaseBody(first); break; case Token.LABEL: Preconditions.checkState(childCount == 2); if (first.getType() != Token.LABEL_NAME && validation) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(first); add(":"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), true); break; // This node is auto generated in anonymous functions and should just get // ignored for our purposes. case Token.SETNAME: break; default: throw new Error("Unknown type " + type + "\n" + n.toStringTree()); } cc.endSourceMapping(n); } /** * Adds a block or expression, substituting a VOID with an empty statement. * This is used for "for (...);" and "if (...);" type statements. * * @param n The node to print. * @param context The context to determine how the node should be

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> parsed as a block, * but "{'x': 'y'}" is parsed as an object literal. */ void addLeftExpr(Node n, int minPrecedence, Context context) { addExpr(n, minPrecedence, context); } void addExpr(Node n, int minPrecedence) { addExpr(n, minPrecedence, Context.OTHER); } private void addExpr(Node n, int minPrecedence, Context context) { if ((NodeUtil.precedence(n.getType()) < minPrecedence) || ((context == Context.IN_FOR_INIT_CLAUSE) && (n.getType() == Token.IN))){ add("("); add(n, clearContextForNoInOperator(context)); add(")"); } else { add(n, context); } } void addList(Node firstInList) { addList(firstInList, true, Context.OTHER); } void addList(Node firstInList, boolean isArrayOrFunctionArgument) { addList(firstInList, isArrayOrFunctionArgument, Context.OTHER); } void addList(Node firstInList, boolean isArrayOrFunctionArgument, Context lhsContext) { for (Node n = firstInList; n != null; n = n.getNext()) { boolean isFirst = n == firstInList; if (isFirst) { addLeftExpr(n, isArrayOrFunctionArgument ? 1 : 0, lhsContext); } else { cc.listSeparator(); addExpr(n, isArrayOrFunctionArgument ? 1 : 0); } } } /** * This function adds a comma-separated list as is specified by an ARRAYLIT * node with the associated skipIndexes array. This is a space optimization * since we avoid creating a whole Node object for each empty array literal * slot. * @param firstInList The first in the node list (chained through the next * property). * @param skipIndexes If not null, then the array of skipped entries in the * array. */ void addList(Node firstInList, int[] skipIndexes) { int nextSlot = 0; int nextSkipSlot = 0; for (Node n = first

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>InList; n != null; n = n.getNext()) { while (skipIndexes != null && nextSkipSlot < skipIndexes.length) { if (nextSlot == skipIndexes[nextSkipSlot]) { cc.listSeparator(); nextSlot++; nextSkipSlot++; } else { break; } } if (n != firstInList) { cc.listSeparator(); } addExpr(n, 1); nextSlot++; } } void addCaseBody(Node caseBody) { cc.beginCaseBody(); add(caseBody); cc.endCaseBody(); } void addAllSiblings(Node n) { for (Node c = n; c != null; c = c.getNext()) { add(c); } } /** Outputs a js string, using the optimal (single/double) quote character */ static String jsString(String s, CharsetEncoder outputCharsetEncoder) { int singleq = 0, doubleq = 0; // could count the quotes and pick the optimal quote character for (int i = 0; i < s.length(); i++) { switch (s.charAt(i)) { case '"': doubleq++; break; case '\'': singleq++; break; } } String doublequote, singlequote; char quote; if (singleq < doubleq) { // more double quotes so escape the single quotes quote = '\''; doublequote = "\""; singlequote = "\\\'"; } else { // more single quotes so escape the doubles quote = '\"'; doublequote = "\\\""; singlequote = "\'"; } return strEscape(s, quote, doublequote, singlequote, "\\\\", outputCharsetEncoder); } /** Escapes regular expression */ static String regexpEscape(String s, CharsetEncoder outputCharsetEncoder) { return strEscape(s, '/', "\"", "'", "\\", outputCharsetEncoder); } /** * Escapes the given string to a double quoted (") JavaScript/JSON string */ static String escapeToDoubleQuotedJsString(String s) { return strEscape(s, '"', "\\\"", "\'", "\\\\", null); } /* If the user doesn't want to specify an output

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> // check is measurably faster than using the CharsetEncoder. if (c > 0x1f && c <= 0x7f) { sb.append(c); } else { // Other characters can be misinterpreted by some js parsers, // or perhaps mangled by proxies along the way, // so we play it safe and unicode escape them. appendHexJavaScriptRepresentation(sb, c); } } } } sb.append(quote); return sb.toString(); } static String identifierEscape(String s) { // First check if escaping is needed at all -- in most cases it isn't. if (NodeUtil.isLatin(s)) { return s; } // Now going through the string to escape non-latin characters if needed. StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); // Identifiers should always go to Latin1/ ASCII characters because // different browser's rules for valid identifier characters are // crazy. if (c > 0x1F && c < 0x7F) { sb.append(c); } else { appendHexJavaScriptRepresentation(sb, c); } } return sb.toString(); } /** Gets the number of children of this node that are non empty. */ private static int getNonEmptyChildCount(Node n) { int i = 0; for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (c.getType() != Token.EMPTY) { i++; } } return i; } /** Gets the first non-empty child of the given node. */ private static Node getFirstNonEmptyChild(Node n) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (c.getType() != Token.EMPTY) { return c; } } return null; } // Information on the current context. Used for disambiguating special cases. // For example, a "{" could indicate the start of an object literal or a // block, depending on the current context. enum

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> s = scope.getParent(); if (s == null) { throw new IllegalArgumentException("Var is not local"); } while (s.getParent() != null) { num += s.getVarCount(); s = s.getParent(); } return num; } /** * Returns whether this is a global variable. */ public boolean isGlobal() { return scope.isGlobal(); } /** * Returns whether this is a local variable. */ public boolean isLocal() { return scope.isLocal(); } /** * Returns whether this is defined in an extern file. */ boolean isExtern() { return input == null || input.isExtern(); } /** * Returns {@code true} if the variable is declared as a constant, * based on the value reported by {@code NodeUtil}. */ public boolean isConst() { return NodeUtil.isConstantName(nameNode); } /** * Returns {@code true} if the variable is declared as a define. * A variable is a define if it is annotaed by {@code @define}. */ public boolean isDefine() { return isDefine; } public Node getInitialValue() { Node parent = getParentNode(); return parent.getType() == Token.FUNCTION ? parent : nameNode.getFirstChild(); } /** * Gets this variable's type. To know whether this type has been inferred, * see {@code #isInferred()}. */ public JSType getType() { return type; } /** * Returns the name node that produced this variable. */ public Node getNameNode() { return nameNode; } /** * Gets the JSDocInfo for the variable. */ public JSDocInfo getJSDocInfo() { return info; } /** * Sets this variable's type. * @throws IllegalStateException if the variable's type is not inferred */ void setType(JSType type) { Preconditions.checkState(isTypeInferred()); this.type = type; } /** * Returns whether this variable's type is inferred. To get the variable's * type, see {@link #getType()}. */ public boolean isTypeInferred() { return typeInferred; }

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>() { return isBottom; } /** * Gets the container node of the scope. This is typically the FUNCTION * node or the global BLOCK/SCRIPT node. */ public Node getRootNode() { return rootNode; } public Scope getParent() { return parent; } Scope getGlobalScope() { Scope result = this; while (result.getParent() != null) { result = result.getParent(); } return result; } @Override public StaticScope<JSType> getParentScope() { return parent; } /** * Gets the type of {@code this} in the current scope. */ public ObjectType getTypeOfThis() { return thisType; } /** * Declares a variable whose type is inferred. * * @param name name of the variable * @param nameNode the NAME node declaring the variable * @param type the variable's type * @param input the input in which this variable is defined. */ Var declare(String name, Node nameNode, JSType type, CompilerInput input) { return declare(name, nameNode, type, input, true); } /** * Declares a variable. * * @param name name of the variable * @param nameNode the NAME node declaring the variable * @param type the variable's type * @param input the input in which this variable is defined. * @param inferred Whether this variable's type is inferred (as opposed * to declared). */ Var declare(String name, Node nameNode, JSType type, CompilerInput input, boolean inferred) { Preconditions.checkState(name != null && name.length() > 0); // Make sure that it's declared only once Preconditions.checkState(vars.get(name) == null); Var var = new Var(inferred); var.name = name; var.nameNode = nameNode; var.type = type; var.scope = this; var.index = vars.size(); var.input = input; // native variables do not have a name node. // TODO(user): make Var abstract and have NativeVar, NormalVar. JSDocInfo info = NodeUtil.getInfoForNameNode(nameNode); var

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>}). */ @Override public SubclassRelationship getClassesDefinedByCall(Node callNode) { Node callName = callNode.getFirstChild(); SubclassType type = typeofClassDefiningName(callName); if (type != null) { Node subclass = null; Node superclass = callNode.getLastChild(); // There are six possible syntaxes for a class-defining method: // SubClass.inherits(SuperClass) // goog.inherits(SubClass, SuperClass) // goog$inherits(SubClass, SuperClass) // SubClass.mixin(SuperClass.prototype) // goog.mixin(SubClass.prototype, SuperClass.prototype) // goog$mixin(SubClass.prototype, SuperClass.prototype) if (callNode.getChildCount() == 2 && callName.getType() == Token.GETPROP) { // SubClass.inherits(SuperClass) subclass = callName.getFirstChild(); } else if (callNode.getChildCount() == 3) { // goog.inherits(SubClass, SuperClass) subclass = callName.getNext(); } // bail out if either of the side of the "inherits" // isn't a real class name. This prevents us from // doing something weird in cases like: // goog.inherits(MySubClass, cond ? SuperClass1 : BaseClass2) if (subclass != null && subclass.isUnscopedQualifiedName() && superclass.isUnscopedQualifiedName()) { // make sure to strip the prototype off of the nodes // to normalize for goog.mixin return new SubclassRelationship( type, stripPrototype(subclass), stripPrototype(superclass)); } } return null; } /** * Determines whether the given node is a class-defining name, like * "inherits" or "mixin." * @return The type of class-defining name, or null. */ private SubclassType typeofClassDefiningName(Node callName) { // Check if the method name matches one of the class-defining methods. String methodName = null; if (callName.getType() == Token.GETPROP) { methodName = callName.getLastChild().getString(); } else if (callName.getType() == Token.NAME) {

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> String name = callName.getString(); int dollarIndex = name.lastIndexOf('$'); if (dollarIndex != -1) { methodName = name.substring(dollarIndex + 1); } } if (methodName != null) { if (methodName.equals("inherits")) { return SubclassType.INHERITS; } else if (methodName.equals("mixin")) { return SubclassType.MIXIN; } } return null; } @Override public boolean isSuperClassReference(String propertyName) { return "superClass_".equals(propertyName); } /** * Given a qualified name node, strip "prototype" off the end. * * Examples of this transformation: * a.b.c => a.b.c * a.b.c.prototype => a.b.c */ private Node stripPrototype(Node qualifiedName) { if (qualifiedName.getType() == Token.GETPROP && qualifiedName.getLastChild().getString().equals("prototype")) { return qualifiedName.getFirstChild(); } return qualifiedName; } /** * Exctracts X from goog.provide('X'), if the applied Node is goog. * * @return The extracted class name, or null. */ @Override public String extractClassNameIfProvide(Node node, Node parent){ return extractClassNameIfGoog(node, parent, "goog.provide"); } /** * Exctracts X from goog.require('X'), if the applied Node is goog. * * @return The extracted class name, or null. */ @Override public String extractClassNameIfRequire(Node node, Node parent){ return extractClassNameIfGoog(node, parent, "goog.require"); } private static String extractClassNameIfGoog(Node node, Node parent, String functionName){ String className = null; if (NodeUtil.isExprCall(parent)) { Node callee = node.getFirstChild(); if (callee != null && callee.getType() == Token.GETPROP) { String qualifiedName = callee.getQualifiedName(); if ((functionName).equals(qualifiedName)) { className = callee.getNext().getString(); } } } return className; }

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> /** * Use closure's implementation. * @return closure's function name for exporting properties. */ @Override public String getExportPropertyFunction() { return "goog.exportProperty"; } /** * Use closure's implementation. * @return closure's function name for exporting symbols. */ @Override public String getExportSymbolFunction() { return "goog.exportSymbol"; } @Override public List<String> identifyTypeDeclarationCall(Node n) { Node callName = n.getFirstChild(); if ("goog.addDependency".equals(callName.getQualifiedName()) && n.getChildCount() >= 3) { Node typeArray = callName.getNext().getNext(); if (typeArray.getType() == Token.ARRAYLIT) { List<String> typeNames = Lists.newArrayList(); for (Node name = typeArray.getFirstChild(); name != null; name = name.getNext()) { if (name.getType() == Token.STRING) { typeNames.add(name.getString()); } } return typeNames; } } return null; } @Override public String identifyTypeDefAssign(Node n) { Node firstChild = n.getFirstChild(); int type = n.getType(); if (type == Token.ASSIGN) { if (TYPEDEF_NAME.equals(n.getLastChild().getQualifiedName())) { return firstChild.getQualifiedName(); } } else if (type == Token.VAR && firstChild.hasChildren()) { if (TYPEDEF_NAME.equals( firstChild.getFirstChild().getQualifiedName())) { return firstChild.getString(); } } return null; } @Override public String getAbstractMethodName() { return "goog.abstractMethod"; } @Override public String getSingletonGetterClassName(Node callNode) { Node callName = callNode.getFirstChild(); if (!"goog.addSingletonGetter".equals(callName.getQualifiedName()) || callName.getChildCount() != 2) { return null; } Node classNode = callName.getNext(); if (!classNode.isQualifiedName()) { return null; } return callName.getNext().getQualifiedName(); } @Override public void applySingletonGetter(

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>FunctionType functionType, FunctionType getterType, ObjectType objectType) { functionType.defineDeclaredProperty("getInstance", getterType, false); functionType.defineDeclaredProperty("instance_", objectType, false); } @Override public String getGlobalObject() { return "goog.global"; } private final Set<String> propertyTestFunctions = ImmutableSet.of( "goog.isDef", "goog.isNull", "goog.isDefAndNotNull", "goog.isString", "goog.isNumber", "goog.isBoolean", "goog.isFunction", "goog.isArray", "goog.isObject"); @Override public boolean isPropertyTestFunction(Node call) { Preconditions.checkArgument(call.getType() == Token.CALL); return propertyTestFunctions.contains( call.getFirstChild().getQualifiedName()); } @Override public ObjectLiteralCast getObjectLiteralCast(NodeTraversal t, Node callNode) { Preconditions.checkArgument(callNode.getType() == Token.CALL); Node callName = callNode.getFirstChild(); if (!"goog.reflect.object".equals(callName.getQualifiedName()) || callName.getChildCount() != 2) { return null; } Node typeNode = callName.getNext(); if (!typeNode.isQualifiedName()) { return null; } Node objectNode = typeNode.getNext(); if (objectNode.getType() != Token.OBJECTLIT) { t.getCompiler().report(JSError.make(t.getSourceName(), callNode, OBJECTLIT_EXPECTED)); return null; } return new ObjectLiteralCast(typeNode.getQualifiedName(), typeNode.getNext()); } /** * {@inheritDoc} */ @Override public boolean isOptionalParameter(Node parameter) { return false; } /** * {@inheritDoc} */ @Override public boolean isVarArgsParameter(Node parameter) { return false; } /** * {@inheritDoc} */ @Override public boolean isPrivate(String name) { return false; } }

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> int charno) { this(nodeType, children); sourcePosition = mergeLineCharNo(lineno, charno); } public Node(int nodeType, Node[] children) { this.type = nodeType; parent = null; if (children.length != 0) { this.first = children[0]; this.last = children[children.length - 1]; for (int i = 1; i < children.length; i++) { if (null != children[i - 1].next) { // fail early on loops. implies same node in array twice throw new IllegalArgumentException("duplicate child"); } children[i - 1].next = children[i]; Preconditions.checkArgument(children[i - 1].parent == null); children[i - 1].parent = this; } Preconditions.checkArgument( children[children.length - 1].parent == null); children[children.length - 1].parent = this; if (null != this.last.next) { // fail early on loops. implies same node in array twice throw new IllegalArgumentException("duplicate child"); } } } public static Node newNumber(double number) { return new NumberNode(number); } public static Node newNumber(double number, int lineno, int charno) { return new NumberNode(number, lineno, charno); } public static Node newString(String str) { return new StringNode(Token.STRING, str); } public static Node newString(int type, String str) { return new StringNode(type, str); } public static Node newString(String str, int lineno, int charno) { return new StringNode(Token.STRING, str, lineno, charno); } public static Node newString(int type, String str, int lineno, int charno) { return new StringNode(type, str, lineno, charno); } public int getType() { return type; } public void setType(int type) { this.type = type; } public boolean hasChildren() { return first != null; } public Node getFirstChild() { return first; } public Node getLastChild

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>() { return last; } public Node getNext() { return next; } public Node getChildBefore(Node child) { if (child == first) return null; Node n = first; while (n.next != child) { n = n.next; if (n == null) throw new RuntimeException("node is not a child"); } return n; } public Node getChildAtIndex(int i) { Node n = first; while (i > 0) { n = n.next; i--; } return n; } public Node getLastSibling() { Node n = this; while (n.next != null) { n = n.next; } return n; } public void addChildToFront(Node child) { Preconditions.checkArgument(child.parent == null); Preconditions.checkArgument(child.next == null); child.parent = this; child.next = first; first = child; if (last == null) { last = child; } } public void addChildToBack(Node child) { Preconditions.checkArgument(child.parent == null); Preconditions.checkArgument(child.next == null); child.parent = this; child.next = null; if (last == null) { first = last = child; return; } last.next = child; last = child; } public void addChildrenToFront(Node children) { for (Node child = children; child != null; child = child.next) { Preconditions.checkArgument(child.parent == null); child.parent = this; } Node lastSib = children.getLastSibling(); lastSib.next = first; first = children; if (last == null) { last = lastSib; } } public void addChildrenToBack(Node children) { for (Node child = children; child != null; child = child.next) { Preconditions.checkArgument(child.parent == null); child.parent = this; } if (last != null) { last.next = children; } last = children.getLastSibling(); if (first == null)

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> sb.append(" : "); sb.append(jsTypeString); } } } } } public String toStringTree() { return toStringTreeImpl(); } private String toStringTreeImpl() { try { StringBuffer s = new StringBuffer(); appendStringTree(s); return s.toString(); } catch (IOException e) { throw new RuntimeException("Should not happen\n" + e); } } public void appendStringTree(Appendable appendable) throws IOException { toStringTreeHelper(this, 0, appendable); } private static void toStringTreeHelper(Node n, int level, Appendable sb) throws IOException { if (Token.printTrees) { for (int i = 0; i != level; ++i) { sb.append(" "); } sb.append(n.toString()); sb.append('\n'); for (Node cursor = n.getFirstChild(); cursor != null; cursor = cursor.getNext()) { toStringTreeHelper(cursor, level + 1, sb); } } } int type; // type of the node; Token.NAME for example Node next; // next sibling private Node first; // first element of a linked list of children private Node last; // last element of a linked list of children /** * Linked list of properties. Since vast majority of nodes would have * no more then 2 properties, linked list saves memory and provides * fast lookup. If this does not holds, propListHead can be replaced * by UintMap. */ private PropListItem propListHead; /** * COLUMN_BITS represents how many of the lower-order bits of * sourcePosition are reserved for storing the column number. * Bits above these store the line number. * This gives us decent position information for everything except * files already passed through a minimizer, where lines might * be longer than 4096 characters. */ public static final int COLUMN_BITS = 12; /** * MAX_COLUMN_NUMBER represents the maximum column number that can * be represented. JSCompiler's modifications to Rhino cause all * tokens located beyond the maximum column to MAX_COLUMN_NUMBER. */ public static final int MAX

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> unlikely. return (new SiblingNodeIterable(start)).iterator(); } } public boolean hasNext() { return current != null; } public Node next() { if (current == null) { throw new NoSuchElementException(); } try { return current; } finally { current = current.getNext(); } } public void remove() { throw new UnsupportedOperationException(); } } //========================================================================== // Accessors public Node getParent() { return parent; } /** * Gets the ancestor node relative to this. * @param level 0 = this, 1 = the parent, etc. */ public Node getAncestor(int level) { Preconditions.checkArgument(level >= 0); Node node = this; while(node != null && level-- > 0) { node = node.getParent(); } return node; } /** * Iterates all of the node's ancestors excluding itself. */ public AncestorIterable getAncestors() { return new AncestorIterable(this.getParent()); } /** * Iterator to go up the ancestor tree. */ public static class AncestorIterable implements Iterable<Node> { private Node cur; /** * @param cur The node to start. */ AncestorIterable(Node cur) { this.cur = cur; } public Iterator<Node> iterator() { return new Iterator<Node>() { public boolean hasNext() { return cur != null; } public Node next() { if (!hasNext()) throw new NoSuchElementException(); Node n = cur; cur = cur.getParent(); return n; } public void remove() { throw new UnsupportedOperationException(); } }; } } /** * Check for one child more efficiently than by iterating over all the * children as is done with Node.getChildCount(). * @return Whether the node has exactly one child. */ public boolean hasOneChild() { return first != null && first == last; } /** * Check for more than one child more efficiently than by iterating over all * the children as is done with Node.getChildCount(). * @return Whether the node more than one child. */ public boolean hasMoreThanOneChild()

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> { return first != null && first != last; } public int getChildCount() { int c = 0; for (Node n = first; n != null; n = n.next) c++; return c; } // Intended for testing and verification only. public boolean hasChild(Node child) { for (Node n = first; n != null; n = n.getNext()) { if (child == n) { return true; } } return false; } /** * Checks if the subtree under this node is the same as another subtree. * Returns null if it's equal, or a message describing the differences. */ public String checkTreeEquals(Node node2) { NodeMismatch diff = checkTreeEqualsImpl(node2); if (diff != null) { return "Node tree inequality:" + "\nTree1:\n" + toStringTree() + "\n\nTree2:\n" + node2.toStringTree() + "\n\nSubtree1: " + diff.nodeA.toStringTree() + "\n\nSubtree2: " + diff.nodeB.toStringTree(); } return null; } /** * If this is a compilation pass and not a test, do not construct error * strings. Instead return true if the trees are equal. */ public boolean checkTreeEqualsSilent(Node node2) { return checkTreeEqualsImpl(node2) == null; } /** * Compare this node to node2 recursively and return the first pair * of nodes that differs doing a preorder depth-first traversal. * Package private for testing. Returns null if the nodes are equivalent. */ NodeMismatch checkTreeEqualsImpl(Node node2) { boolean eq = false; if (type == node2.getType() && getChildCount() == node2.getChildCount() && getClass() == node2.getClass()) { eq = this.isEquivalentTo(node2); } if (!eq) { return new NodeMismatch(this, node2); } NodeMismatch res = null; Node n, n2; for (n = first, n2 = node2.first; res == null && n != null; n

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> = n.next, n2 = n2.next) { res = n.checkTreeEqualsImpl(n2); if (res != null) { return res; } } return res; } /** * Checks if the subtree under this node is the same as another subtree * including types. Returns null if it's equal, or a message describing the * differences. */ public boolean checkTreeTypeAwareEqualsSilent(Node node2) { return checkTreeTypeAwareEqualsImpl(node2) == null; } /** * Compare this node to node2 recursively and return the first pair * of nodes that differs doing a preorder depth-first traversal. * Package private for testing. Returns null if the nodes are equivalent. */ NodeMismatch checkTreeTypeAwareEqualsImpl(Node node2) { boolean eq = false; if (type == node2.getType() && getChildCount() == node2.getChildCount() && getClass() == node2.getClass() && Objects.equal(jsType, node2.getJSType())) { eq = this.isEquivalentTo(node2); } if (!eq) { return new NodeMismatch(this, node2); } NodeMismatch res = null; Node n, n2; for (n = first, n2 = node2.first; res == null && n != null; n = n.next, n2 = n2.next) { res = n.checkTreeTypeAwareEqualsImpl(n2); if (res != null) { return res; } } return res; } public static String tokenToName(int token) { switch (token) { case Token.ERROR: return "error"; case Token.EOF: return "eof"; case Token.EOL: return "eol"; case Token.ENTERWITH: return "enterwith"; case Token.LEAVEWITH: return "leavewith"; case Token.RETURN: return "return"; case Token.GOTO: return "goto"; case Token.IFEQ: return "ifeq"; case Token.IFNE: return "ifne"; case Token.SETNAME: return "setname"; case Token.BIT

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>.INC: case Token.DEC: case Token.EXPORT: case Token.IMPORT: case Token.IF: case Token.ELSE: case Token.SWITCH: case Token.WHILE: case Token.DO: case Token.FOR: case Token.BREAK: case Token.CONTINUE: case Token.VAR: case Token.CONST: case Token.WITH: case Token.CATCH: case Token.FINALLY: case Token.BLOCK: case Token.LABEL: case Token.TARGET: case Token.LOOP: case Token.JSR: case Token.SETPROP_OP: case Token.SETELEM_OP: case Token.LOCAL_BLOCK: case Token.SET_REF_OP: return true; default: return false; } } /** * This function takes a set of GETPROP nodes and produces a string that is * each property separated by dots. If the node ultimately under the left * sub-tree is not a simple name, this is not a valid qualified name. * * @return a null if this is not a qualified name, or a dot-separated string * of the name and properties. */ public String getQualifiedName() { if (type == Token.NAME) { return getString(); } else if (type == Token.GETPROP) { String left = getFirstChild().getQualifiedName(); if (left == null) { return null; } return left + "." + getLastChild().getString(); } else if (type == Token.THIS) { return "this"; } else { return null; } } /** * Returns whether a node corresponds to a simple or a qualified name, such * as <code>x</code> or <code>a.b.c</code> or <code>this.a</code>. */ public boolean isQualifiedName() { switch (getType()) { case Token.NAME: case Token.THIS: return true; case Token.GETPROP: return getFirstChild().isQualifiedName(); default: return false; } } /** * Returns whether a node corresponds to a simple or a qualified name * without a "this" reference, such as <code>a.

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>b.c</code>, but not * <code>this.a</code>. */ public boolean isUnscopedQualifiedName() { switch (getType()) { case Token.NAME: return true; case Token.GETPROP: return getFirstChild().isUnscopedQualifiedName(); default: return false; } } //========================================================================== // Mutators /** * Removes this node from its parent. Equivalent to: * node.getParent().removeChild(); */ public Node detachFromParent() { Preconditions.checkState(parent != null); parent.removeChild(this); return this; } /** * Removes the first child of Node. Equivalent to: * node.removeChild(node.getFirstChild()); * @return The removed Node. */ public Node removeFirstChild() { Node child = first; if (child != null) { removeChild(child); } return child; } /** * @return A Node that is the head of the list of children. */ public Node removeChildren() { Node children = first; for (Node child = first; child != null; child = child.getNext()) { child.parent = null; } first = null; last = null; return children; } /** * Removes all children from this node and isolates the children from each * other. */ public void detachChildren() { for (Node child = first; child != null; ) { Node nextChild = child.getNext(); child.parent = null; child.next = null; child = nextChild; } first = null; last = null; } public Node removeChildAfter(Node prev) { Preconditions.checkArgument(prev.parent == this, "prev is not a child of this node."); Preconditions.checkArgument(prev.next != null, "no next sibling."); Node child = prev.next; prev.next = child.next; if (child == last) last = prev; child.next = null; child.parent = null; return child; } /** * @return A detached clone of the Node, specifically excluding its * children. */ public Node cloneNode()

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> { Node result; try { result = (Node) super.clone(); result.next = null; result.first = null; result.last = null; result.parent = null; } catch (CloneNotSupportedException e) { throw new RuntimeException(e.getMessage()); } return result; } /** * @return A detached clone of the Node and all its children. */ public Node cloneTree() { Node result = cloneNode(); for (Node n2 = getFirstChild(); n2 != null; n2 = n2.getNext()) { Node n2clone = n2.cloneTree(); n2clone.parent = result; if (result.last != null) { result.last.next = n2clone; } if (result.first == null) { result.first = n2clone; } result.last = n2clone; } return result; } /** * Copies source file and name information from the other * node given to the current node. Used for maintaining * debug information across node append and remove operations. * @return this */ public Node copyInformationFrom(Node other) { if (getProp(ORIGINALNAME_PROP) == null) { putProp(ORIGINALNAME_PROP, other.getProp(ORIGINALNAME_PROP)); } if (getProp(SOURCEFILE_PROP) == null) { putProp(SOURCEFILE_PROP, other.getProp(SOURCEFILE_PROP)); sourcePosition = other.sourcePosition; } return this; } /** * Copies source file and name information from the other node to the * entire tree rooted at this node. * @return this */ public Node copyInformationFromForTree(Node other) { copyInformationFrom(other); for (Node child = getFirstChild(); child != null; child = child.getNext()) { child.copyInformationFromForTree(other); } return this; } //========================================================================== // Custom annotations public JSType getJSType() { return jsType; } public void setJSType(JSType jsType) { this.jsType = jsType; } public FileLevelJsDocBuilder

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> null) { switch (parent.getType()) { case Token.FOR: // Only traverse the body of the for loop. return n == parent.getLastChild(); // Skip the conditions. case Token.IF: case Token.WHILE: case Token.WITH: return n != parent.getFirstChild(); case Token.DO: return n != parent.getFirstChild().getNext(); // Only traverse the body of the cases case Token.SWITCH: case Token.CASE: case Token.CATCH: case Token.LABEL: return n != parent.getFirstChild(); case Token.FUNCTION: return n == parent.getFirstChild().getNext().getNext(); case Token.CONTINUE: case Token.BREAK: case Token.EXPR_RESULT: case Token.VAR: case Token.RETURN: case Token.THROW: return false; case Token.TRY: /* Just before we are about to visit the second child of the TRY node, * we know that we will be visiting either the CATCH or the FINALLY. * In other words, we know that the post order traversal of the TRY * block has been finished, no more exceptions can be caught by the * handler at this TRY block and should be taken out of the stack. */ if (n == parent.getFirstChild().getNext()) { Preconditions.checkState(exceptionHandler.peek() == parent); exceptionHandler.pop(); } } } return true; } @Override public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.IF: handleIf(n); return; case Token.WHILE: handleWhile(n); return; case Token.DO: handleDo(n); return; case Token.FOR: handleFor(n); return; case Token.SWITCH: handleSwitch(n); return; case Token.CASE: handleCase(n); return; case Token.DEFAULT: handleDefault(n); return; case Token.BLOCK: case Token.SCRIPT: handleStmtList(n); return; case Token.FUNCTION: handleFunction(n); return; case Token.EXPR_RESULT: handle

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>Expr(n); return; case Token.THROW: handleThrow(n); return; case Token.TRY: handleTry(n); return; case Token.CATCH: handleCatch(n); return; case Token.BREAK: handleBreak(n); return; case Token.CONTINUE: handleContinue(n); return; case Token.RETURN: handleReturn(n); return; case Token.WITH: handleWith(n); return; case Token.LABEL: return; default: handleStmt(n); return; } } private void handleIf(Node node) { Node thenBlock = node.getFirstChild().getNext(); Node elseBlock = thenBlock.getNext(); createEdge(node, Branch.ON_TRUE, computeFallThrough(thenBlock)); if (elseBlock == null) { createEdge(node, Branch.ON_FALSE, computeFollowNode(node)); // not taken branch } else { createEdge(node, Branch.ON_FALSE, computeFallThrough(elseBlock)); } connectToPossibleExceptionHandler( node, NodeUtil.getConditionExpression(node)); } private void handleWhile(Node node) { // Control goes to the first statement if the condition evaluates to true. createEdge(node, Branch.ON_TRUE, computeFallThrough(node.getFirstChild().getNext())); // Control goes to the follow() if the condition evaluates to false. createEdge(node, Branch.ON_FALSE, computeFollowNode(node)); connectToPossibleExceptionHandler( node, NodeUtil.getConditionExpression(node)); } private void handleDo(Node node) { // The first edge can be the initial iteration as well as the iterations // after. createEdge(node, Branch.ON_TRUE, computeFallThrough(node.getFirstChild())); // The edge that leaves the do loop if the condition fails. createEdge(node, Branch.ON_FALSE, computeFollowNode(node)); connectToPossibleExceptionHandler( node, NodeUtil.getConditionExpression(node)); } private void handleFor(Node forNode) { if (forNode.getChildCount() == 4) { // We have for (init; cond; iter) {

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> body } Node init = forNode.getFirstChild(); Node cond = init.getNext(); Node iter = cond.getNext(); Node body = iter.getNext(); // After initialization, we transfer to the FOR which is in charge of // checking the condition (for the first time). createEdge(init, Branch.UNCOND, forNode); // The edge that transfer control to the beginning of the loop body. createEdge(forNode, Branch.ON_TRUE, computeFallThrough(body)); // The edge to end of the loop. createEdge(forNode, Branch.ON_FALSE, computeFollowNode(forNode)); // The end of the body will have a unconditional branch to our iter // (handled by calling computeFollowNode of the last instruction of the // body. Our iter will jump to the forNode again to another condition // check. createEdge(iter, Branch.UNCOND, forNode); connectToPossibleExceptionHandler(init, init); connectToPossibleExceptionHandler(forNode, cond); connectToPossibleExceptionHandler(iter, iter); } else { // We have for (item in collection) { body } Node item = forNode.getFirstChild(); Node collection = item.getNext(); Node body = collection.getNext(); // The edge that transfer control to the beginning of the loop body. createEdge(forNode, Branch.ON_TRUE, computeFallThrough(body)); // The edge to end of the loop. createEdge(forNode, Branch.ON_FALSE, computeFollowNode(forNode)); connectToPossibleExceptionHandler(forNode, collection); } } private void handleSwitch(Node node) { // Transfer to the first non-DEFAULT CASE. if there are none, transfer // to the DEFAULT or the EMPTY node. Node next = getNextSiblingOfType( node.getFirstChild().getNext(), Token.CASE, Token.EMPTY); if (next != null) { // Has at least one CASE or EMPTY createEdge(node, Branch.UNCOND, next); } else { // Has no CASE but possibly a DEFAULT if (node.getFirstChild().getNext() != null) { createEdge(node, Branch.UNCOND, node.getFirstChild().getNext()); } else { // No CASE, no DEFAULT createEdge(node

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>, Branch.UNCOND, computeFollowNode(node)); } } connectToPossibleExceptionHandler(node, node.getFirstChild()); } private void handleCase(Node node) { // Case is a bit tricky....First it goes into the body if condition is true. createEdge(node, Branch.ON_TRUE, node.getFirstChild().getNext()); // Look for the next CASE, skipping over DEFAULT. Node next = getNextSiblingOfType(node.getNext(), Token.CASE); if (next != null) { // Found a CASE Preconditions.checkState(next.getType() == Token.CASE); createEdge(node, Branch.ON_FALSE, next); } else { // No more CASE found, go back and search for a DEFAULT. Node parent = node.getParent(); Node deflt = getNextSiblingOfType( parent.getFirstChild().getNext(), Token.DEFAULT); if (deflt != null) { // Has a DEFAULT createEdge(node, Branch.ON_FALSE, deflt); } else { // No DEFAULT found, go to the follow of the SWITCH. createEdge(node, Branch.ON_FALSE, computeFollowNode(node)); } } connectToPossibleExceptionHandler(node, node.getFirstChild()); } private void handleDefault(Node node) { // Directly goes to the body. It should not transfer to the next case. createEdge(node, Branch.UNCOND, node.getFirstChild()); } private void handleWith(Node node) { // Directly goes to the body. It should not transfer to the next case. createEdge(node, Branch.UNCOND, node.getLastChild()); connectToPossibleExceptionHandler(node, node.getFirstChild()); } private void handleStmtList(Node node) { Node parent = node.getParent(); // Special case, don't add a block of empty CATCH block to the graph. if (node.getType() == Token.BLOCK && parent != null && parent.getType() == Token.TRY && NodeUtil.getCatchBlock(parent) == node && !NodeUtil.hasCatchHandler(node)) { return; } // A block transfer control to its first child if it is not empty. Node child = node.getFirstChild(); // Function declarations are skipped since

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> control doesn't go into that // function (unless it is called) while (child != null && child.getType() == Token.FUNCTION) { child = child.getNext(); } if (child != null) { createEdge(node, Branch.UNCOND, computeFallThrough(child)); } else { createEdge(node, Branch.UNCOND, computeFollowNode(node)); } // Synthetic blocks if (parent != null) { switch (parent.getType()) { case Token.DEFAULT: case Token.CASE: case Token.TRY: break; default: if (node.getType() == Token.BLOCK && node.isSyntheticBlock()) { Node next = node.getLastChild(); if (next != null) { createEdge(node, Branch.SYN_BLOCK, computeFallThrough(next)); } } break; } } } private void handleFunction(Node node) { // A block transfer control to its first child if it is not empty. Preconditions.checkState(node.getChildCount() >= 3); createEdge(node, Branch.UNCOND, computeFallThrough(node.getFirstChild().getNext().getNext())); Preconditions.checkState(exceptionHandler.peek() == node); exceptionHandler.pop(); } private void handleExpr(Node node) { createEdge(node, Branch.UNCOND, computeFollowNode(node)); connectToPossibleExceptionHandler(node, node); } private void handleThrow(Node node) { connectToPossibleExceptionHandler(node, node); } private void handleTry(Node node) { createEdge(node, Branch.UNCOND, node.getFirstChild()); } private void handleCatch(Node node) { createEdge(node, Branch.UNCOND, node.getLastChild()); } private void handleBreak(Node node) { String label = null; // See if it is a break with label. if (node.hasChildren()) { label = node.getFirstChild().getString(); } Node cur; Node lastJump; Node parent = node.getParent(); /* * Continuously look up the ancestor tree for the BREAK target or the target * with the corresponding label and connect to it. If along the path we *

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> discover a FINALLY, we will connect the BREAK to that FINALLY. From then * on, we will just record the control flow changes in the finallyMap. This * is due to the fact that we need to connect any node that leaves its own * FINALLY block to the outer FINALLY or the BREAK's target but those nodes * are not known yet due to the way we traverse the nodes. */ for (cur = node, lastJump = node; !isBreakTarget(cur, parent, label); cur = parent, parent = parent.getParent()) { if (cur.getType() == Token.TRY && NodeUtil.hasFinally(cur)) { if (lastJump == node) { createEdge(lastJump, Branch.UNCOND, computeFallThrough( cur.getLastChild())); } else { finallyMap.put(lastJump, computeFallThrough(cur.getLastChild())); } lastJump = cur; } Preconditions.checkState(parent != null, "Cannot find break target."); } if (lastJump == node) { createEdge(lastJump, Branch.UNCOND, computeFollowNode(cur)); } else { finallyMap.put(lastJump, computeFollowNode(cur)); } } private void handleContinue(Node node) { String label = null; if (node.hasChildren()) { label = node.getFirstChild().getString(); } Node cur; Node lastJump; // Similar to handBreak's logic with a few minor variation. Node parent = node.getParent(); for (cur = node, lastJump = node; !isContinueTarget(cur, parent, label); cur = parent, parent = parent.getParent()) { if (cur.getType() == Token.TRY && NodeUtil.hasFinally(cur)) { if (lastJump == node) { createEdge(lastJump, Branch.UNCOND, cur.getLastChild()); } else { finallyMap.put(lastJump, computeFallThrough(cur.getLastChild())); } lastJump = cur; } Preconditions.checkState(parent != null, "Cannot find continue target."); } Node iter = cur; if (cur.getChildCount() == 4) { iter = cur.getFirstChild().getNext().getNext

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>(); } if (lastJump == node) { createEdge(node, Branch.UNCOND, iter); } else { finallyMap.put(lastJump, iter); } } private void handleReturn(Node node) { Node lastJump = null; for (Iterator<Node> iter = exceptionHandler.iterator(); iter.hasNext();) { Node curHandler = iter.next(); if (NodeUtil.isFunction(curHandler)) { break; } if (NodeUtil.hasFinally(curHandler)) { if (lastJump == null) { createEdge(node, Branch.UNCOND, curHandler.getLastChild()); } else { finallyMap.put(lastJump, computeFallThrough(curHandler.getLastChild())); } lastJump = curHandler; } } if (node.hasChildren()) { connectToPossibleExceptionHandler(node, node.getFirstChild()); } if (lastJump == null) { createEdge(node, Branch.UNCOND, null); } else { finallyMap.put(lastJump, null); } } private void handleStmt(Node node) { // Simply transfer to the next line. createEdge(node, Branch.UNCOND, computeFollowNode(node)); connectToPossibleExceptionHandler(node, node); } private Node computeFollowNode(Node node) { return computeFollowNode(node, node); } /** * Computes the follow() node of a given node and its parent. There is a side * effect when calling this function. If this function computed an edge that * exists a FINALLY, it'll attempt to connect the fromNode to the outer * FINALLY according to the finallyMap. * * @param fromNode The original source node since {@code node} is changed * during recursion. * @param node The node that follow() should compute. */ private Node computeFollowNode(Node fromNode, Node node) { /* * This is the case where: * * 1. Parent is null implies that we are transferring control to the end of * the script. * * 2. Parent is a function implies that we are transferring control back to * the caller of the function. * *

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>3. If the node is a return statement, we should also transfer control * back to the caller of the function. * * 4. If the node is root then we have reached the end of what we have been * asked to traverse. * * In all cases we should transfer control to a "symbolic return" node. * This will make life easier for DFAs. */ Node parent = node.getParent(); if (parent == null || parent.getType() == Token.FUNCTION || node == root) { return null; } // If we are just before a IF/WHILE/DO/FOR: switch (parent.getType()) { // The follow() of any of the path from IF would be what follows IF. case Token.IF: return computeFollowNode(fromNode, parent); case Token.CASE: case Token.DEFAULT: // After the body of a CASE, the control goes to the body of the next // case, without having to go to the case condition. if (parent.getNext() != null) { if (parent.getNext().getType() == Token.CASE) { return parent.getNext().getFirstChild().getNext(); } else if (parent.getNext().getType() == Token.DEFAULT) { return parent.getNext().getFirstChild(); } else { Preconditions.checkState(false, "Not reachable"); } } else { return computeFollowNode(fromNode, parent); } break; case Token.FOR: if (NodeUtil.isForIn(parent)) { return parent; } else { return parent.getFirstChild().getNext().getNext(); } case Token.WHILE: case Token.DO: return parent; case Token.TRY: // If we are coming out of the TRY block... if (parent.getFirstChild() == node) { if (NodeUtil.hasFinally(parent)) { // and have FINALLY block. return computeFallThrough(parent.getLastChild()); } else { // and have no FINALLY. return computeFollowNode(fromNode, parent); } // CATCH block. } else if (NodeUtil.getCatchBlock(parent) == node){ if (NodeUtil.hasFinally(parent)) { // and have FINALLY block.

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> return computeFallThrough(node.getNext()); } else { return computeFollowNode(fromNode, parent); } // If we are coming out of the FINALLY block... } else if (parent.getLastChild() == node){ for (Node finallyNode : finallyMap.get(parent)) { createEdge(fromNode, Branch.UNCOND, finallyNode); } return computeFollowNode(fromNode, parent); } } // Now that we are done with the special cases follow should be its // immediate sibling, unless its sibling is a function Node nextSibling = node.getNext(); // Skip function declarations because control doesn't get pass into it. while (nextSibling != null && nextSibling.getType() == Token.FUNCTION) { nextSibling = nextSibling.getNext(); } if (nextSibling != null) { return computeFallThrough(nextSibling); } else { // If there are no more siblings, control is transfered up the AST. return computeFollowNode(fromNode, parent); } } /** * Computes the destination node of n when we want to fallthough into the * subtree of n. We don't always create a CFG edge into n itself because of * DOs and FORs. */ private static Node computeFallThrough(Node n) { switch (n.getType()) { case Token.DO: return computeFallThrough(n.getFirstChild()); case Token.FOR: if (NodeUtil.isForIn(n)) { return n; } return computeFallThrough(n.getFirstChild()); case Token.LABEL: return computeFallThrough(n.getLastChild()); default: return n; } } /** * Connects the two nodes in the control flow graph. * * @param fromNode Source. * @param toNode Destination. */ private void createEdge(Node fromNode, ControlFlowGraph.Branch branch, Node toNode) { cfg.createNode(fromNode); cfg.createNode(toNode); cfg.connectIfNotFound(fromNode, branch, toNode); } /** * Connects cfgNode to the proper CATCH block if target subtree might throw * an exception. If there are FINALLY blocks reached before a CATCH, it will

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> * make the corresponding entry in finallyMap. */ private void connectToPossibleExceptionHandler(Node cfgNode, Node target) { if (mayThrowException(target) && !exceptionHandler.isEmpty()) { Node lastJump = cfgNode; for (Node handler : exceptionHandler) { if (NodeUtil.isFunction(handler)) { return; } Preconditions.checkState(handler.getType() == Token.TRY); Node catchBlock = NodeUtil.getCatchBlock(handler); if (!NodeUtil.hasCatchHandler(catchBlock)) { // No catch but a FINALLY. if (lastJump == cfgNode) { createEdge(cfgNode, Branch.ON_EX, handler.getLastChild()); } else { finallyMap.put(lastJump, handler.getLastChild()); } } else { // Has a catch. if (lastJump == cfgNode) { createEdge(cfgNode, Branch.ON_EX, catchBlock); return; } else { finallyMap.put(lastJump, catchBlock); } } lastJump = handler; } } } /** * Get the next sibling (including itself) of one of the given types. */ private static Node getNextSiblingOfType(Node first, int ... types) { for (Node c = first; c != null; c = c.getNext()) { for (int type : types) { if (c.getType() == type) { return c; } } } return null; } /** * Checks if target is actually the break target of labeled continue. The * label can be null if it is an unlabeled break. */ private static boolean isBreakTarget( Node target, Node parent, String label) { return isBreakStructure(target, label != null) && matchLabel(parent, label); } /** * Checks if target is actually the continue target of labeled continue. The * label can be null if it is an unlabeled continue. */ private static boolean isContinueTarget( Node target, Node parent, String label) { return isContinueStructure(target) && matchLabel(parent, label); } /** * Check if label is actually referencing the target control structure. If * label is null, it always returns

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> true. */ private static boolean matchLabel(Node target, String label) { if (label == null) { return true; } while (target.getType() == Token.LABEL) { if (target.getFirstChild().getString().equals(label)) { return true; } target = target.getParent(); } return false; } /** * Determines if the subtree might throw an exception. */ private static boolean mayThrowException(Node n) { switch (n.getType()) { case Token.CALL: case Token.GETPROP: case Token.GETELEM: case Token.THROW: case Token.NEW: case Token.ASSIGN: case Token.INC: case Token.DEC: case Token.INSTANCEOF: return true; case Token.FUNCTION: return false; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (!ControlFlowGraph.isEnteringNewCfgNode(c) && mayThrowException(c)) { return true; } } return false; } /** * Determines whether the given node can be terminated with a BREAK node. */ static boolean isBreakStructure(Node n, boolean labeled) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: case Token.SWITCH: return true; case Token.BLOCK: case Token.IF: case Token.TRY: return labeled; default: return false; } } /** * Determines whether the given node can be advanced with a CONTINUE node. */ static boolean isContinueStructure(Node n) { switch (n.getType()) { case Token.FOR: case Token.DO: case Token.WHILE: return true; default: return false; } } /** * A {@link ControlFlowGraph} which provides a node comparator based on the * pre-order traversal of the AST. */ private static class AstControlFlowGraph extends ControlFlowGraph<Node> { private final Map<DiGraphNode<Node, Branch>, Integer> priorities; /** * Constructor. * @param entry The entry node. * @param

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> return new FunctionType(this, name, source, parameters, returnType, null, null, true, false); } /** * Creates an interface function type. * @param name the function's name * @param source the node defining this function. Its type * ({@link Node#getType()}) must be {@link Token#FUNCTION}. */ public FunctionType createInterfaceType(String name, Node source) { return new FunctionType(this, name, source); } /** * Creates a parameterized type. */ public ParameterizedType createParameterizedType( ObjectType objectType, JSType parameterType) { return new ParameterizedType(this, objectType, parameterType); } /** * Identifies the name of an enum before we actually declare it. */ public void identifyEnumName(String name) { enumTypeNames.add(name); } /** * Creates a RecordType from the nodes representing said record type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ public JSType createRecordTypeFromNodes(Node n, String sourceName, StaticScope<JSType> scope) { RecordTypeBuilder builder = new RecordTypeBuilder(this); // For each of the fields in the record type. for (Node fieldTypeNode = n.getFirstChild(); fieldTypeNode != null; fieldTypeNode = fieldTypeNode.getNext()) { // Get the property's name. Node fieldNameNode = fieldTypeNode; boolean hasType = false; if (fieldTypeNode.getType() == Token.COLON) { fieldNameNode = fieldTypeNode.getFirstChild(); hasType = true; } String fieldName = fieldNameNode.getString(); // TODO(user): Move this into the lexer/parser. // Remove the string literal characters around a field name, // if any. if (fieldName.startsWith("'") || fieldName.startsWith("\"")) { fieldName = fieldName.substring(1, fieldName.length() - 1); } // Get the property's type. JSType fieldType = null; if (hasType) { // We have a declared type. fieldType = createFromTypeNodes( fieldTypeNode.getLastChild(), source

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>Name, scope); } else { // Otherwise, the type is UNKNOWN. fieldType = getNativeType(JSTypeNative.UNKNOWN_TYPE); } // Add the property to the record. builder.addProperty(fieldName, fieldType); } return builder.build(); } /** * Creates a JSType from the nodes representing a type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ public JSType createFromTypeNodes(Node n, String sourceName, StaticScope<JSType> scope) { switch (n.getType()) { case Token.LC: // Record type. return createRecordTypeFromNodes(n.getFirstChild(), sourceName, scope); case Token.BANG: // Not nullable return createFromTypeNodes(n.getFirstChild(), sourceName, scope) .restrictByNotNullOrUndefined(); case Token.QMARK: // Nullable return createNullableType( createFromTypeNodes(n.getFirstChild(), sourceName, scope)); case Token.EQUALS: // Optional return createOptionalType( createFromTypeNodes(n.getFirstChild(), sourceName, scope)); case Token.ELLIPSIS: // Var args return createOptionalType( createFromTypeNodes(n.getFirstChild(), sourceName, scope)); case Token.STAR: // The AllType return getNativeType(ALL_TYPE); case Token.LB: // Array type // TODO(nicksantos): Enforce membership restrictions on the Array. return getNativeType(ARRAY_TYPE); case Token.PIPE: // Union type UnionTypeBuilder builder = new UnionTypeBuilder(this); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { builder.addAlternate(createFromTypeNodes(child, sourceName, scope)); } return builder.build(); case Token.EMPTY: // When the return value of a function is not specified return getNativeType(UNKNOWN_TYPE); case Token.VOID: // Only allowed in the return value of a function. return getNativeType(VOID_TYPE); case Token.STRING: JSType namedType = getType(scope, n.getString(), source

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>Name, n.getLineno(), n.getCharno()); if ((namedType instanceof ObjectType) && !(enumTypeNames.contains(n.getString()))) { Node typeList = n.getFirstChild(); if (typeList != null && ("Array".equals(n.getString()) || "Object".equals(n.getString()))) { JSType parameterType = createFromTypeNodes( typeList.getLastChild(), sourceName, scope); namedType = new ParameterizedType( this, (ObjectType) namedType, parameterType); if (typeList.hasMoreThanOneChild()) { JSType indexType = createFromTypeNodes( typeList.getFirstChild(), sourceName, scope); namedType = new IndexedType( this, (ObjectType) namedType, indexType); } } return createNullableType(namedType); } else { return namedType; } case Token.FUNCTION: ObjectType thisType = null; Node current = n.getFirstChild(); if (current.getType() == Token.THIS) { Node thisNode = current.getFirstChild(); thisType = ObjectType.cast( createFromTypeNodes(thisNode, sourceName, scope) .restrictByNotNullOrUndefined()); if (thisType == null) { reporter.warning( ScriptRuntime.getMessage0("msg.jsdoc.function.thisnotobject"), sourceName, thisNode.getLineno(), "", thisNode.getCharno()); } current = current.getNext(); } FunctionParamBuilder paramBuilder = new FunctionParamBuilder(this); if (current.getType() == Token.LP) { Node args = current.getFirstChild(); for (Node arg = current.getFirstChild(); arg != null; arg = arg.getNext()) { if (arg.getType() == Token.ELLIPSIS) { if (arg.getChildCount() == 0) { paramBuilder.addVarArgs(getNativeType(UNKNOWN_TYPE)); } else { paramBuilder.addVarArgs( createFromTypeNodes( arg.getFirstChild(), sourceName, scope)); } } else { JSType type = createFromTypeNodes(arg, sourceName, scope); if (arg.getType() == Token.EQUALS)

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> { boolean addSuccess = paramBuilder.addOptionalParams(type); if (!addSuccess) { reporter.warning( ScriptRuntime.getMessage0("msg.jsdoc.function.varargs"), sourceName, arg.getLineno(), "", arg.getCharno()); } } else { paramBuilder.addRequiredParams(type); } } } current = current.getNext(); } JSType returnType = createFromTypeNodes(current, sourceName, scope); return new FunctionType(this, null, null, paramBuilder.build(), returnType, thisType, null); } throw new IllegalStateException( "Unexpected node in type expression: " + n.toString()); } /** * Sets the template type name. */ public void setTemplateTypeName(String name) { templateTypeName = name; templateType = new TemplateType(this, name); } /** * Clears the template type name. */ public void clearTemplateTypeName() { templateTypeName = null; templateType = null; } }

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>EnteringNewCfgNode(n); } } /** * @return True if n should be represented by a new CFG node in the control * flow graph. */ public static boolean isEnteringNewCfgNode(Node n) { Node parent = n.getParent(); switch (parent.getType()) { case Token.BLOCK: case Token.SCRIPT: case Token.TRY: case Token.FINALLY: return true; case Token.FUNCTION: // A function node represents the start of a function where the name // is bleed into the local scope and parameters has been assigned // to the formal argument names. The node includes the name of the // function and the LP list since we assume the whole set up process // is atomic without change in control flow. The next change of // control is going into the function's body represent by the second // child. return n != parent.getFirstChild().getNext(); case Token.WHILE: case Token.DO: case Token.IF: // Theses control structure is represented by its node that holds the // condition. Each of them is a branch node based on its condition. return NodeUtil.getConditionExpression(parent) != n; case Token.FOR: // The FOR(;;) node differs from other control structure in that // it has a initialization and a increment statement. Those // two statements have its corresponding CFG nodes to represent them. // The FOR node represents the condition check for each iteration. // That way the following: // for(var x = 0; x < 10; x++) { } has a graph that is isomorphic to // var x = 0; while(x<10) { x++; } if (NodeUtil.isForIn(parent)) { return n == parent.getLastChild(); } else { return NodeUtil.getConditionExpression(parent) != n; } case Token.SWITCH: case Token.CASE: case Token.CATCH: case Token.WITH: return n != parent.getFirstChild(); default: return false; } } }

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> * */ final class ArrowType extends JSType { private static final long serialVersionUID = 1L; final Node parameters; JSType returnType; ArrowType(JSTypeRegistry registry, Node parameters, JSType returnType) { super(registry); this.parameters = parameters; this.returnType = returnType; } @Override public boolean isSubtype(JSType other) { if (!(other instanceof ArrowType)) { return false; } ArrowType that = (ArrowType) other; // this.returnType <: that.returnType (covariant) // If the return type is null, this is equivalent to unknown so we do not // base our decision on that. if (this.returnType != null && that.returnType != null && !this.returnType.isSubtype(that.returnType)) { return false; } // that.paramType[i] <: this.paramType[i] (contravariant) // TODO(nicksantos): This is incorrect. It should be invariant. // Follow up with closure team on how to fix this without everyone // hating on us. // // If the parameter list is null, this is equivalent of ?... so we do not // base our decision on that. if (this.parameters != null && that.parameters != null) { Node thisParam = parameters.getFirstChild(); Node thatParam = that.parameters.getFirstChild(); while (thisParam != null && thatParam != null) { JSType thisParamType = thisParam.getJSType(); if (thisParamType != null) { JSType thatParamType = thatParam.getJSType(); if (thatParamType == null || !thatParamType.isSubtype(thisParamType)) { return false; } } boolean thisIsVarArgs = thisParam.isVarArgs(); boolean thatIsVarArgs = thatParam.isVarArgs(); // don't advance if we have variable arguments if (!thisIsVarArgs) { thisParam = thisParam.getNext(); } if (!thatIsVarArgs) { thatParam = thatParam.getNext(); } // both var_args indicates the end if (thisIsVarArgs && thatIsVarArgs) { thisParam = null; that

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>Param = null; } } // Right now, the parser's type system doesn't have a good way // to model optional arguments. // // Suppose we have // function f(number, number) {} // function g(number) {} // If the second arg of f is optional, then f is a subtype of g, // but g is not a subtype of f. // If the second arg of f is required, then g is a subtype of f, // but f is not a subtype of g. // // Until we model optional params, let's just punt on this. // If one type has more arguments than the other, we won't check them. // // NOTE(nicksantos): This is described in Draft 2 of the ES4 spec, // Section 3.4.6: Subtyping Function Types. It seems really // strange but I haven't thought a lot about the implementation. } return true; } @Override public boolean equals(Object object) { // Please keep this method in sync with the hashCode() method below. if (!(object instanceof ArrowType)) { return false; } ArrowType that = (ArrowType) object; // if both return types are specified, then they should be equal if (returnType == null) { if (that.returnType != null) { return false; } } else { if (that.returnType == null) { return false; } if (!returnType.equals(that.returnType)) { return false; } } // if both types include parameters, the lists should be the same if (parameters == null) { return that.parameters == null; } else if (that.parameters == null) { return false; } Node thisParam = parameters.getFirstChild(); Node otherParam = that.parameters.getFirstChild(); while (thisParam != null && otherParam != null) { JSType thisParamType = thisParam.getJSType(); JSType otherParamType = otherParam.getJSType(); if (thisParamType != null) { // Both parameter lists give a type for this param, it should be equal if (otherParamType != null && !thisParamType.equals(otherParamType)) {

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> return false; } } else { if (otherParamType != null) { return false; } } thisParam = thisParam.getNext(); otherParam = otherParam.getNext(); } // One of the parameters is null, so the types are only equal if both // parameter lists are null (they are equal). return thisParam == otherParam; } @Override public int hashCode() { int hashCode = 0; if (returnType != null) { hashCode += returnType.hashCode(); } if (parameters != null) { Node param = parameters.getFirstChild(); while (param != null) { JSType paramType = param.getJSType(); if (paramType != null) { hashCode += paramType.hashCode(); } param = param.getNext(); } } return hashCode; } @Override public JSType getLeastSupertype(JSType that) { throw new UnsupportedOperationException(); } @Override public JSType getGreatestSubtype(JSType that) { throw new UnsupportedOperationException(); } @Override public TernaryValue testForEquality(JSType that) { throw new UnsupportedOperationException(); } @Override public <T> T visit(Visitor<T> visitor) { throw new UnsupportedOperationException(); } @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { return BooleanLiteralSet.TRUE; } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { returnType = safeResolve(returnType, t, scope); if (parameters != null) { for (Node paramNode = parameters.getFirstChild(); paramNode != null; paramNode = paramNode.getNext()) { paramNode.setJSType(paramNode.getJSType().resolve(t, scope)); } } return this; } }

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS>.kind = Kind.INTERFACE; this.typeOfThis = new InstanceObjectType(registry, this); } @Override public boolean isInstanceType() { // The universal constructor is its own instance, bizarrely. return equals(registry.getNativeType(U2U_CONSTRUCTOR_TYPE)); } @Override public boolean isConstructor() { return kind == Kind.CONSTRUCTOR; } @Override public boolean isInterface() { return kind == Kind.INTERFACE; } @Override public boolean isOrdinaryFunction() { return kind == Kind.ORDINARY; } @Override public boolean isFunctionType() { return true; } @Override public boolean canBeCalled() { return true; } public Iterable<Node> getParameters() { Node n = getParametersNode(); if (n != null) { return n.children(); } else { return Collections.emptySet(); } } /** Gets an LP node that contains all params. May be null. */ public Node getParametersNode() { return call == null ? null : call.parameters; } /** Gets the minimum number of arguments that this function requires. */ public int getMinArguments() { // NOTE(nicksantos): There are some native functions that have optional // parameters before required parameters. This algorithm finds the position // of the last required parameter. int i = 0; int min = 0; for (Node n : getParameters()) { i++; if (!n.isOptionalArg() && !n.isVarArgs()) { min = i; } } return min; } /** * Gets the maximum number of arguments that this function requires, * or Integer.MAX_VALUE if this is a variable argument function. */ public int getMaxArguments() { Node params = getParametersNode(); if (params != null) { Node lastParam = params.getLastChild(); if (lastParam == null || !lastParam.isVarArgs()) { return params.getChildCount(); } } return Integer.MAX_VALUE; } public JSType getReturnType() { return call == null ? null : call.returnType; } /** * Gets the {@code prototype}

Closure, 142

<FILEB>
<CHANGES>
if (scope.getRootNode().getFirstChild().getNext().getChildCount() == 2) {
liveness.markAllParametersEscaped();
}
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (!(option == WhitespaceOption.PRESERVE &&
token == JsDocToken.ANNOTATION)) {
<CHANGEE>
<CHANGES>
}
<CHANGEE>
<FILEE>
<FILEB> } @Override public void enterScope(NodeTraversal t) { // TODO(user): We CAN do this in the global scope, just need to be // careful when something is exported. Liveness uses bit-vector for live // sets so I don't see compilation time will be a problem for running this // pass in the global scope. Scope scope = t.getScope(); if (scope.isGlobal()) { return; } ControlFlowGraph<Node> cfg = t.getControlFlowGraph(); LiveVariablesAnalysis liveness = new LiveVariablesAnalysis(cfg, scope, compiler); // If the function has exactly 2 params, mark them as escaped. This is // a work-around for an IE bug where it throws an exception if you // write to the parameters of the callback in a sort(). See: // http://code.google.com/p/closure-compiler/issues/detail?id=58 <CHANGES> <CHANGEE> liveness.analyze(); UndiGraph<Var, Void> interferenceGraph = computeVariableNamesInterferenceGraph( t, cfg, liveness.getEscapedLocals()); GraphColoring<Var, Void> coloring = new GreedyGraphColoring<Var, Void>(interferenceGraph, coloringTieBreaker); coloring.color(); colorings.push(coloring); } @Override public void exitScope(NodeTraversal t) { <FILEE> <FILEB> builder.append(' '); } builder.append('*'); } token = next(); continue; case EOL: if (option != WhitespaceOption.SINGLE_LINE) { builder.append("\n"); } ignoreStar = true; token = next(); continue; case ANNOTATION: case EOC: case EOF: // When we're capturing a license block, annotations // in the block are ok. <CHANGES> <CHANGEE> String multilineText = builder.toString(); if (option != WhitespaceOption.PRESERVE) { multilineText = multilineText.trim(); } int endLineno = stream.getLineno(); int endCharno = stream.getCharno(); if (multilineText.length() > 0) { jsdocBuilder.markText(mult<SCANS> new StringBuilder(32); b.append("function ("); int paramNum = (call == null || call.parameters == null) ? 0 : call.parameters.getChildCount(); boolean hasKnownTypeOfThis = !typeOfThis.isUnknownType(); if (hasKnownTypeOfThis) { b.append("this:"); b.append(typeOfThis.toString()); } if (paramNum > 0) { if (hasKnownTypeOfThis) { b.append(", "); } Node p = call.parameters.getFirstChild(); if (p.isVarArgs()) { appendVarArgsString(b, p.getJSType()); } else { b.append(p.getJSType().toString()); } p = p.getNext(); while (p != null) { b.append(", "); if (p.isVarArgs()) { appendVarArgsString(b, p.getJSType()); } else { b.append(p.getJSType().toString()); } p = p.getNext(); } } b.append(")"); if (call != null && call.returnType != null) { b.append(": "); b.append(call.returnType); } return b.toString(); } /** Gets the string representation of a var args param. */ private void appendVarArgsString(StringBuilder builder, JSType paramType) { if (paramType.isUnionType()) { // Remove the optionalness from the var arg. paramType = ((UnionType) paramType).getRestrictedUnion( registry.getNativeType(JSTypeNative.VOID_TYPE)); } builder.append("...[").append(paramType.toString()).append("]"); } /** * A function is a subtype of another if their call methods are related via * subtyping and {@code this} is a subtype of {@code that} with regard to * the prototype chain. */ @Override public boolean isSubtype(JSType that) { if (this.equals(that)) { return true; } if (that.isFunctionType()) { if (((FunctionType) that).isInterface()) { // Any function can be assigned to an interface function. return true; } if (this